Burn-Link ·

把阅后即焚链接贴进会生成预览的聊天之后,阅读次数为什么不一定已经被用掉

频道里先跳出一张卡片,不等于接收方已经读过密文。聊天软件为了生成预览,会向页面发一次 HTTP GET;这次抓取通常只读标题和摘要,不会点「打开并查看」,也不会带走地址栏 # 后面的密钥。下面把「生成卡片」和「消耗一次阅读」拆开,并写出当场能对照的步骤。不是阅后即焚创建页的操作说明书。

对照预览抓取 可当场核对 打开即用
00 / 目录

先分清 卡片抓的是哪一层

本篇只回答「贴进会生成预览的聊天之后,阅读次数为什么不一定已经被用掉」。不是阅后即焚的表单说明,也不是「# 为什么不进服务器日志」那一层。

01 / 抓取

预览卡片抓的是 哪一层

卡片出现在你按下发送的前后几秒。动作来自聊天软件的服务器,不是接收方的手指。

把一条 https://…/s.html?id=…#… 贴进 Slack、Discord、企业微信或微信之后,输入框或消息下方常常先跳出一张小卡片:标题、一两句摘要、有时还有图标。很多人把这张卡片读成「链接已经被打开过了」。顺序其实相反。聊天软件要先向这个地址发一次 HTTP GET,从返回的 HTML 里抽出 og:titleog:description 或普通 <title>,再画卡片。接收方此时可能还没点进去。

Slack 把这件事写进了官方说明。默认情况下,用户和 Slack 应用发出的、带完整网址的消息都会展开链接;媒体内容也可以展开。说明见 Slack 文档《Unfurling links in messages》。专门负责展开链接的机器人自称 Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)。同一页 Slack Robots 写明:它会尽量少取页面(使用 HTTP Range),目标是 oEmbed、Twitter Card 和 Open Graph 标签;如果标签指向图片、视频或音频,才会再取那份文件做校验。响应大约缓存 30 分钟。文档没有写「执行页面上的 JavaScript」或「替用户点按钮」。

浏览器发出这次 GET 时,# 后面的内容不会进入请求行。IETF RFC 3986 §3.5 把 fragment 定义成客户端标识,不参与服务器对 URI 的处理。所以预览机器人即便完整复制了你在聊天框里看到的字符串,真正打到 UsePwd 的请求通常只到 s.html?id={编号}。密钥为什么不进访问日志,见已有文章URL 的 # 片段为什么不会出现在服务器日志里。本篇要补的是另一层:这次不带密钥的 GET,算不算「读过一次」。

先记一句

预览要问的不是「聊天软件有没有看见这串网址」,而是「这一次 GET 有没有走到取密文的接口」。卡片画出来,只证明标题被读过;密文还在不在,要另查。

02 / 分层

第一次 GET 为什么 不等于读过

若工具把「谁来取密文」和「谁来打开落地页」绑成同一次请求,预览机器人就会把唯一的一次阅读用掉。分层之后,这两步不再是同一个动作。

不少一次性链接把密文直接挂在被预览的那个 URL 上:机器人 GET 一次,服务器就返回正文并删除记录。接收方随后点开,看到的是「已被阅读」或 404。这个失败很常见,原因不是 Slack「恶意烧掉秘密」,而是实现把 RFC 里本该安全、可缓存的 GET,当成了「销毁密文」的动作。

UsePwd 的阅后即焚把三步拆开。创建时,当前标签页用 Web Crypto 按 AES-256-GCM 加密,明文上限 32 KB;上传字段只有密文、过期时间和阅读次数。过期时间可选 1 小时、24 小时、7 天,或「仅阅后焚毁」;阅读次数 1–10,默认 1。链接形态是 s.html?id={id}#{key}。创建页和阅读页都打开即用,双方都不用注册。服务器只暂存密文,看不到明文,也没有账号可以按用户找回。

接收方打开阅读页时,页面先向 /api/secrets/{id}/status 询问密文还在不在。这一步只返回 activeburnedexpired,不增加阅读计数,也不删除记录。只有点了「打开并查看」,浏览器才会请求 /api/secrets/{id} 取回密文,并在本机用 # 后的密钥解密。达到创建时设定的次数后,服务器删除密文;再取会得到 410。预览机器人停在落地页 HTML,常见情况下既没有密钥,也不会去点那个按钮。

落地页 GET
s.html?id=编号

返回静态阅读页。标题是「打开阅后即焚链接」,不含明文。预览卡片通常停在这一层。

状态查询
/secrets/{id}/status

只回答还在、已焚毁或已过期。反复查询也不会把次数加一。

取密文
/secrets/{id}

这才计入一次阅读。默认 1 次后删除密文。阅读页上对应「打开并查看」。

阅读页还写了一句可以当场核对的提示:点按钮「可能消耗一次阅读次数」。没有脚本就无法解密,页面用 <noscript> 说明了这一点。预览机器人若只读 HTML、不执行脚本,连状态查询都不会发出;即便某家内置浏览器执行了脚本,也只先走到「密文还在」,仍要等人点按钮。

03 / 机器人

哪些机器人会来、拿走什么

能画卡片的软件很多。它们拿走的是页面抬头,不是 # 后面那串密钥,更不是解密后的明文。

Slack 的链接展开机器人、Discord 用来生成嵌入卡片的抓取、微信和企业微信里把网址收成一张卡片的服务端请求,都属于同一类:先 GET 页面,再从 HTML 里找标题和摘要。Slack 另外说明,它不把 robots.txt 当成传统爬虫规则来遵守,因为展开链接是「替发链接的人办事」,不是顺着站点乱爬。你在阅读页加 noindex,挡得住搜索引擎收录,挡不住频道里的那张卡片。

UsePwd 的阅读页本身是临时密文场景,带 noindex, nofollow,也不进站点地图。页面标题固定写成「打开阅后即焚链接」,描述只说明解密在当前标签页完成、密钥在 # 后面。预览卡片若成功生成,频道里看到的就是这句通用说明,不会出现你写入的密码、Token 或工单原文。这和「服务器日志里有没有密钥」是两件能同时成立的事:卡片证明有人 GET 过落地页;日志里仍然没有 fragment。

Slack 写过,同一 URL 的展开结果大约缓存 30 分钟。短时间里把同一条测试链接反复贴进频道,不一定会看到第二次抓取。要核对「机器人来过没有」,应看你自己的访问日志里有没有 Slackbot-LinkExpanding 这类 User-Agent,而不是盯着卡片刷新几次。日志里出现对 s.html 的 GET,仍然不能推论「/api/secrets/{id} 已被调用」。

落地页 HTML 01 预览常取这一层。标题通用,不含明文,也不带 # 密钥。
状态接口 02 只有执行了页面脚本才会问。问了也不消耗次数。
取密文接口 03 对应「打开并查看」。普通卡片抓取到不了这一层。
聊天记录里的完整网址 04 这是另一份副本。次数没掉,不等于链接只存在于接收方脑中。
04 / 会烧掉

什么情况下次数 真的会被用掉

「不一定用掉」不是「永远用不掉」。能点到「打开并查看」的访问,才会走进取密文那一层。

最常见的消耗者是接收方本人:打开完整链接,看到「密文还在」,再点「打开并查看」。默认次数是 1。这一次成功之后,服务器删除密文;同一条链接再打开会提示已被焚毁。若创建时把次数设到 2 或更高,前几次取回后密文仍在,直到计数达到上限。过期时间到了也会删除:默认 24 小时,也可以是 1 小时、7 天;选「仅阅后焚毁」则只按次数删,不按钟点删。

第二类是会执行脚本并且模拟点击的环境。部分邮件安全网关、链接沙箱会在隔离浏览器里打开网址,有的还会点页面上的主按钮。那已经不是「生成一张卡片」,而是另一次完整的、像人一样的访问。若沙箱点了「打开并查看」,次数会被用掉,接收方随后看到焚毁态。这一层要用网关自己的报告核对,不能从聊天卡片反推。

第三类是把取密文的接口地址直接发出去。阅读页地址是 s.html?id=;真正消耗次数的是 /api/secrets/{id}。普通用户不会复制后一条。若有人把接口 URL 贴进会预览的频道,而那家工具又把第一次 GET 当成取密文,次数会在预览阶段被用掉。UsePwd 的分享形态不走这条地址,创建页复制的是带 # 的阅读页链接。

不要读反

预览没烧掉次数,不等于秘密只存在于接收方那里。聊天软件仍保存完整网址,工作区管理员还可能导出记录。次数限制的是服务器上密文的寿命,不是频道里那串地址还能被谁打开。

05 / 核对

当场怎么 核对照预览

目标不是证明「全世界的聊天软件都不会读密文」,而是证明:卡片出现之后,取密文的接口还没有被调用。

  1. 01
    准备一条不会用于真实交接的测试内容

    不要写入正在使用的 API Key 或登录密码。例如打开阅后即焚,输入 PreviewCard-20260905,次数保持默认 1,过期保持 24 小时。页面打开即用。下面的步骤只为对照预览和接口,不把这段字交给同事当正式密钥。

  2. 02
    记下完整链接,先自己打开阅读页

    生成后应看到 s.html?id=# 两段。用同一条链接在当前浏览器打开。页面应停在「密文还在」,并出现「打开并查看」。先不要点按钮。按 F12 打开网络面板,应能看到对 /api/secrets/{id}/status 的请求;此时不应出现对 /api/secrets/{id} 且无 /status 后缀的取密文请求。

  3. 03
    把同一条链接贴进会生成预览的测试频道

    用一个只有你自己的 Slack 频道、Discord 私信或测试群。贴出后等卡片出现。卡片标题应接近「打开阅后即焚链接」,不应出现 PreviewCard-20260905。回到阅读页刷新,或换一个未点过按钮的窗口再打开同一链接,应仍停在「密文还在」。若这里已经变成「已被焚毁」,说明有别的访问走到了取密文接口,而不是「卡片本身等于阅读」。

  4. 04
    需要时再对照访问日志里的 User-Agent

    若你能看到站点访问日志,预览阶段常见的是对 /cn/s.html?id=… 的 GET,User-Agent 可能含 Slackbot-LinkExpanding。同一时间不应出现对 /api/secrets/{id} 的成功取回。Slack 对同一 URL 大约缓存 30 分钟,短时间重复粘贴不一定产生第二次抓取。

  5. 05
    最后才点「打开并查看」,确认次数被用掉

    现在点按钮。网络面板应出现取密文的请求,页面展示测试原文。关闭后再打开同一链接,应提示已被焚毁。这一步用来对照「按钮之后」和「卡片之后」不是同一状态。测完即可丢掉这条链接,不要把它改派给真实接收方。

若你只是要把新密钥交给另一个人,不要先把明文贴进频道「试一张卡片」。测试内容和正式内容分开。正式交接仍然走阅后即焚;环境变量泄露后的第二轮风险,见云平台环境变量被拖走之后,新密钥为什么不该再发到聊天窗口。那篇写的是「不要把 API Key 本身贴进可搜索的记录」;本篇写的是「链接贴进去之后,预览抓取停在哪一层」。

06 / 边界

次数没掉,聊天里还 留下什么

把「卡片没有烧掉密文」读成「这条链接已经从频道里消失」,会漏掉几条同样能核对的边界。

聊天软件保存的是你贴出去的整串地址。谁能搜索历史、导出工作区、打开备份,谁就能再次打开阅读页。次数还在时,后打开的人仍可能点「打开并查看」并读到明文;次数用尽后,记录里留下的是一条已焚毁的链接,加上当时的卡片标题。管理员导出改不了服务器上已经删除的密文,但改不掉「当时频道里出现过完整网址」这件事。

截图和转发是另一份副本。有人把阅读成功后的明文截下来,或把链接转到另一个会预览的群,边界就从「这一次 GET」变成「下一跳还经过谁」。阅后即焚限制服务器上密文的寿命和次数,不限制接收方是否截图。UsePwd 没有账号和密码库,也不能按用户找回丢失的链接。需要长期保存或多设备同步时,应使用专门的密码管理器。身份说明见关于

文件是第三条路径。单文件不超过 5 GB 的本地备份,走文件加密盒,输出 .lock / .enc,文件默认不上传。口令本身用密码生成器在 6–128 位之间生成,再用密码检测看强度。这些页面和本篇一样打开即用。不要把「预览没烧掉次数」理解成「贴进任意频道都安全」:频道记录和预览抓取不是同一层。

07 / 问答

预览之后 常被问到的

下面四条只回答本篇的边界,不重复创建页上的按钮说明。

看到卡片是不是已经读过了 01 通常不是。卡片只说明落地页被 GET 过。次数要等有人点「打开并查看」。
微信预览会不会执行脚本 02 生成卡片的服务端抓取通常只读 HTML。对方点进内置浏览器才执行脚本,且仍要再点按钮才会取密文。
# 后面的密钥预览机器人看得到吗 03 HTTP 请求带不走片段。机器人按网址去抓时,通常只带到问号后的编号。
创建和阅读要注册吗 04 不要。打开即用。顶栏右侧只有语言切换,没有登录或密码库入口。
08 / 下一步

读完去 核对照一张预览卡片

文章回答「预览之后次数为什么不一定已经被用掉」。要当场生成一条不用于真实交接的测试链接再贴进测试频道,打开阅后即焚即可,不必先注册。