另请参阅 翻译。
Copyright © 2026 World Wide Web Consortium. W3C® liability, trademark and permissive document license rules apply.
WebSub 提供了一种通用机制,用于在任何类型 Web 内容的发布者及其 订阅者之间基于 HTTP Webhook 进行通信。订阅请求会通过中心进行中继,中心会验证并 核验该请求。然后,当新的和更新的内容可用时,中心会将其分发给订阅者。 WebSub 以前称为 PubSubHubbub。
本节描述了本文档在其发布时的状态。当前 W3C 出版物列表以及本技术报告的最新修订版可在 W3C 标准与草案 索引中找到。
此更新后的规范在安全注意事项一节中加入了与跨站脚本(XSS)风险相关的缓解措施, 并致谢了报告该问题的人员。
本文档由 Social Web Working Group 作为 推荐标准并使用 推荐标准 轨道发布。
W3C 推荐将本规范作为 Web 的 标准进行广泛部署。
W3C 推荐标准是一种规范,它经过广泛的 共识构建后,由 W3C 及其成员背书,并且 获得工作组成员对实现作出的 免版税许可 承诺。
本文档由一个根据 W3C 专利 政策 运作的小组制作。 W3C 维护着一个 与该小组交付物相关的任何专利披露的公开列表; 该页面还包含 披露专利的说明。实际知晓某项专利,且认为该专利包含 必要权利要求 的个人,必须根据 W3C 专利政策第 6 节 披露相关信息。
本文档受 2025 年 8 月 18 日 W3C 流程文档 管辖。
(本节是非规范性的。)
此协议的较早版本称为 PubSubHubbub:
本文档中的关键词 "MUST"、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、 "SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"MAY" 和 "OPTIONAL" 应按 [RFC2119] 中所述解释。
除标记为非规范性的章节外,本规范中的所有编写指南、图表、示例和注释 都是非规范性的。本规范中的其他所有内容都是规范性的。
本文档中的关键词 MAY、MUST、MUST NOT、OPTIONAL、RECOMMENDED、REQUIRED、SHALL、SHALL NOT、 SHOULD 和 SHOULD NOT 应按 BCP 14 [RFC2119] [RFC8174] 中所述解释,但仅当它们像此处所示那样全部以 大写字母出现时才如此。
WebSub 描述了三种角色:发布者、订阅者和中心。本节描述 每个角色的一致性准则。
一致的订阅者:
一致的中心:
本规范在每个特性至少具有两个独立且可互操作的实现后退出 CR 阶段。 每个特性可以由不同的一组产品实现。不要求所有特性必须由单一产品实现。 为了此准则的目的,我们定义以下术语:
WebSub 发布者是在一个或多个资源 URL 上通告主题和中心 URL 的实现。一致性准则见上文一致性 类别。
WebSub 订阅者是这样一种实现:给定资源 URL 时发现中心和主题 URL, 在中心订阅更新,并接受来自中心的内容分发请求。该 订阅者MAY 支持经认证的内容分发。其 一致性准则见上文一致性类别。
WebSub 中心是处理订阅请求并在相应主题 URL 已更新时将内容分发给 订阅者的实现。中心MUST 支持带有秘密值的订阅请求,并在被请求时递送经认证的请求。中心MUST 在请求中递送主题 URL 的完整内容,并且MAY 在内容类型支持时将负载缩减为差异。其 一致性准则见上文一致性类别。
每个实现必须由不同的一方开发,且不能共享、复用或派生自 另一个合格实现所使用的代码。与本规范实现无关的代码部分 不受此要求约束。
当中心执行订阅者所请求的已定义动作,订阅者根据该特性从中心获得预期响应, 并且中心向订阅者发送预期响应时,订阅者和中心实现 被认为在某个特性上是可互操作的。
为了评估退出准则,以下每一项都被视为一项特性:
发现机制旨在识别至少 2 个 URL。
发布者可能希望通告并发布到多个中心,以实现容错和 冗余。如果一个中心未能将更新传播到文档,则使用多个独立 中心是提高向订阅者递送可能性的一种方式。因此,订阅者可以订阅 一个或多个所通告的中心。
该协议当前支持以下发现机制。发布者MUST 实现其中至少一种:
由于 <link> 多年来一直被限制为放在 <head> 中,一些消费代码可能只检查 <head>。因此,更稳健的做法是只将 <link> 标签放在 HTML <head> 中,而不是放在 <body> 中。
GET /feed HTTP/1.1
Host: example.com
HTTP/1.1 200 Ok
Content-type: text/html
Link: <https://hub.example.com/>; rel="hub"
Link: <http://example.com/feed>; rel="self"
<!doctype html>
<html>
<head>
<link rel="hub" href="https://hub.example.com/">
<link rel="self" href="http://example.com/feed">
</head>
<body>
...
</body>
</html>
执行发现时,订阅者MUST 按以下顺序实现这 两种发现机制,并在首次匹配时停止:
出于实际目的,rel=self URL 只提供单一 表示很重要。由于中心无法知道订阅者在发现时请求了什么媒体类型([RFC6838])或语言, 因此它将无法使用文档的适当表示来递送内容。
但是,可以通过根据初始发现请求中使用的 HTTP 标头返回适当的 rel=self URL 来执行内容协商。例如,带有包含 application/json 的 Accept 标头对 /feed 的请求,可以返回 /feed.json 作为 rel=self 值。
下面的示例说明了主题 URL 如何根据所发送的 Accept 标头返回不同的 Link 标头。
GET /feed HTTP/1.1
Host: example.com
Accept: application/json
HTTP/1.1 200 Ok
Content-type: application/json
Link: </feed.json>; rel="self"
Link: <https://hub.example.com/>; rel="hub"
{
"items": [...]
}
GET /feed HTTP/1.1
Host: example.com
Accept: text/html
HTTP/1.1 200 Ok
Content-type: text/html
Link: </feed.html>; rel="self"
Link: <https://hub.example.com/>; rel="hub"
<html>
...
类似地,也可以使用该技术,根据 Accept-Language 标头所请求的语言返回不同的 rel=self URL。
GET /feed HTTP/1.1
Host: example.com
Accept-Language: de-DE
HTTP/1.1 200 Ok
Content-type: application/json
Link: </feed-de.json>; rel="self"
Link: <https://hub.example.com/>; rel="hub"
{
"items": [...]
}
订阅主题 URL 由四个部分组成,这些部分可以立即按顺序发生,也可以存在延迟。
取消订阅以相同方式工作,只是会更改一个参数以表明 取消订阅的意愿。另外,中心不会与发布者一起验证取消订阅请求。
订阅由订阅者向中心 URL 发出 HTTPS 或 HTTP POST [RFC7231] 请求来发起。此请求MUST 具有 application/x-www-form-urlencoded 的 Content-Type 标头(见 [HTML5] 第 4.10.22.6 节),MUST 使用 UTF-8 [Encoding] 作为文档字符 编码,并且MUST 在其主体中使用以下参数,并按相应格式设置:
订阅者MAY 还包含其他 HTTP [RFC7230] 请求参数,以及中心所需的 HTTP [RFC7230] 标头。
中心MUST 忽略它们不理解的其他请求参数。
中心MUST 允许订阅者重新请求已经 激活的订阅。随后每次向中心发出的订阅或取消订阅请求MUST 覆盖特定主题 URL 和回调 URL 组合的先前订阅状态,但前提是 该动作已被验证(第 4.3 节)。如果验证失败, 订阅状态MUST 保持不变。这是必需的,以便订阅者可以 在租约秒数期限结束之前续订其订阅,而不会出现任何中断。该 订阅者MAY 在将来的订阅中使用新的 hub.secret 值,并且MAY 在没有 hub.secret 的情况下创建新的订阅。
主题和回调 URL MAY 使用 HTTP [RFC7230] 或 HTTPS [RFC2818] 方案。主题 URL MUST 是发布者在发现阶段通过 Self Link Header 通告的那个。 (见第 3 节)。如果主题 URL 与发布者通告的 URL 不对应, 中心MAY 拒绝订阅。除此之外,主题 URL 可以按照 URL 规范 [URL] 自由形式给出。中心MUST 始终对这些 URL 参数中的非保留字符进行解码;见 [URL] 中关于“百分号编码字节”的第 1.2 节。
回调 URL SHOULD 是不可猜测的唯一 URL([capability-urls]),并且SHOULD 使用 HTTPS [RFC7230]。 在确认订阅和递送内容时,回调 URL 充当从中心到订阅者的认证。 此外,回调SHOULD 是唯一的 (不为多个中心重复使用),并在续订订阅时更改。
回调 URL MAY 包含任意查询字符串参数(例如 ?foo=bar&red=fish)。中心MUST 在订阅验证期间保留查询字符串, 方法是使用 &(和号)字符连接,将新参数追加到列表末尾。 名称与验证请求所使用名称重叠的现有参数不会被覆盖。发送内容分发 请求时,中心将向回调 URL 发出 POST 请求,并在请求的 URL 部分包含任何查询字符串参数, 而不是作为 POST 主体参数。
如果中心 URL 支持 WebSub,并且能够处理订阅或取消订阅请求,则它 MUST 以 HTTP [RFC7231] 202 "Accepted" 响应来响应订阅请求,以表明该请求已被接收,并将由中心进行 验证(第 5.3 节)和校验(第 5.2 节)。中心SHOULD 尽快执行意图的验证和校验。
如果中心在订阅请求中发现任何错误,则MUST 返回适当的 HTTP [RFC7231] 错误响应码(4xx 或 5xx)。发生错误时,中心SHOULD 在响应主体中以纯文本形式返回错误描述,用于帮助客户端开发者理解错误。 这并非旨在显示给最终用户。中心MAY 根据其自身策略 决定拒绝某些回调 URL 或主题 URL(例如,域授权、主题 URL 端口号)。 但是,由于意图的验证和校验是异步步骤,其逻辑上在 HTTP 响应返回之后开始, 因此 HTTP 响应MUST NOT 依赖于验证或校验的过程或结果。
如果中心 URL 无法处理订阅或取消订阅请求,它MAY 重定向到另一个支持 WebSub 的中心。它通过产生 HTTP [RFC7231] 307(临时重定向)或 308(永久重定向)响应来这样做。它MUST 还 至少包含一个 HTTP [RFC7230] Location 标头,其中包含订阅者要使用的中心的首选 URL 引用。 订阅者预期会在新的中心 URL 重试订阅或取消订阅。
订阅MAY 由中心进行校验,中心可能需要更多详细信息来 接受或拒绝订阅。中心MAY 还会与发布者核对 是否应接受该订阅。
如果(以及当)订阅被接受,中心MUST 执行订阅者的意图验证。
如果(以及当)订阅被拒绝,中心MUST 通过向订阅请求中给出的 订阅者回调 URL 发送 HTTP [RFC7231](或 HTTPS [RFC2818])GET 请求来通知订阅者。此请求附加以下查询字符串参数 (格式见 [URL] 第 4 节):
订阅MAY 在任何时候被中心拒绝(即使它之前已被 接受)。订阅者SHOULD 随后认为该订阅 不再可能。
为了防止攻击者代表订阅者创建不想要的订阅(或 取消订阅所需的订阅),中心必须确保订阅者确实发送了订阅 请求。
中心通过向订阅请求中给出的订阅者回调 URL 发送 HTTP [RFC7231](或 HTTPS [RFC2818])GET 请求来验证订阅请求。 此请求附加以下查询字符串参数(格式见 [URL] 第 4 节):
[#x2B] | [#2D-#x39] | [#x3D] | [#41-#x5A] | [#x5F] | [#x61-#x7A]。
订阅者MUST 确认 hub.topic 对应于它希望执行的 待处理订阅或取消订阅。如果是,则订阅者MUST 以 HTTP 成功(2xx)代码响应,并使响应主体等于 hub.challenge 参数。如果订阅者不同意该动作,则 订阅者MUST 以 404 "Not Found" 响应进行响应。
订阅者MUST 为订阅确认 响应使用安全媒体类型,以防止反射式代码注入(反射式 XSS)攻击。
中心MUST 将其他服务器响应码(3xx、4xx、5xx)视为 验证请求失败。如果订阅者返回 HTTP [RFC7231] 成功(2xx),但内容主体与 hub.challenge 参数不匹配,则中心 MUST 也认为验证失败。
中心MAY 使 hub.lease_seconds 等于 订阅者在其订阅请求中传递的值,但MAY 根据 中心策略更改该值。为了维持订阅,订阅者MUST 在 hub.lease_seconds 秒经过之前,在中心重新请求订阅。
中心MUST 强制执行租约过期,并且MUST NOT 发出永久租约持续时间。
本规范使用 GET 与 POST 来区分订阅请求的确认/拒绝 和内容递送。虽然从 Web 架构角度看,这不被认为是“最佳实践”, 但它确实使回调 URL 的实现更简单。由于内容 分发请求的 POST 主体可以是任意内容类型,并且只包含文档的实际内容, 因此使用 GET 与 POST 的区别在这两种模式之间切换处理,可使 实现更简单。
当主题已更新时,发布者MUST 通知其先前指定的中心。 中心和发布者可以约定任何机制,只要中心最终能够将 更新后的负载发送给订阅者即可。
发布者通知中心的具体机制留待未指定。例如, 一些现有公共中心 [1] [2] [3] 要求发布者发送带有键 hub.mode="publish" 和 hub.url=(已更新资源的 URL) 的 POST 请求。
如果发布者希望将现有订阅迁移到新的主题 URL,它可以使用 HTTP 重定向来做到这一点。
rel=self URL 和新的中心,并
将在新的中心订阅新的主题 URL。这不需要先前中心的任何参与,并且无论发布者是否也更换中心都适用。
当某个主题 URL 有新内容可用时,中心会向订阅者发送内容分发请求。 该请求是从中心到订阅者回调 URL 的 HTTP [RFC7231](或 HTTPS [RFC2818])POST 请求。 POST 请求的 HTTP 主体MUST 包含内容 分发通知的负载。内容分发请求MUST 具有 与主题的 Content-Type 对应的 Content-Type 标头,并且MUST 包含主题 URL 的完整内容,但允许如下所述的例外。
对于 Atom([RFC4287])和 RSS([RSS-2.0])feed,中心MAY 从 feed
中移除已经递送过的 atom:entry 或 rss:item
元素。
该请求MUST 包含至少一个 Link 标头 [RFC5988],其 rel=hub 指向与正在更新的主题相关联的中心。它MUST 还包含一个 Link 标头 [RFC5988],其 rel=self 设置为 正在更新的主题的规范 URL。中心SHOULD 将这些标头合并为 一个 Link 标头 [RFC5988]。所有这些 URL 都是发现 过程(第 3 节)产生的 URL。订阅者MUST NOT 使用这些 Link 标头来识别与内容分发请求对应的订阅,因为 Link 标头是与主题内容相关联的元数据,而不是与任何特定订阅相关联。 例如,内容分发请求中的主题 URL 可能不同于最初订阅的主题 URL。
订阅者的回调 URL MUST 返回 HTTP [RFC7231] 2xx 响应码以表示成功。订阅者的回调 URL MAY 返回 HTTP 410 代码以表示订阅已被删除,并且如果中心收到该代码作为响应,中心MAY 终止该订阅。中心MUST 将所有其他订阅者响应码视为失败;这意味着订阅者MUST NOT 使用 HTTP 重定向来移动订阅。订阅者SHOULD 尽快响应内容分发请求;其成功响应码SHOULD 仅表示已收到消息,而不是确认订阅者已成功处理该消息。 来自订阅者的响应主体MUST 被中心忽略。中心SHOULD 在其自行施加的重试次数和总体重试时间限制内, 重试内容分发请求。 当失败的递送超出中心的限制时,中心会停止尝试递送该通知。中心 MUST 使订阅保持活动直到租约持续时间结束,并且如果 主题发布了新的更新,则MUST 继续尝试向 先前失败的订阅者重新递送。
如果订阅者在其订阅请求中为 hub.secret 提供了值,则中心MUST 生成负载的 HMAC 签名,并将该签名包含在 内容分发请求的请求标头中。X-Hub-Signature 标头的值 MUST 采用 method=signature 形式,其中 method 是 可识别的算法名称之一,而 signature 是签名的十六进制表示。 签名MUST 使用 HMAC 算法 [RFC6151] 计算,其中请求主体作为数据,hub.secret 作为密钥。
以下算法是最初注册的算法名称,基于发布时 [FIPS-PUB-180-4] 注册表的内容。
将来,可能会指定一个扩展,允许订阅者指明其 可用于验证的算法。在撰写本文时,大多数中心使用 SHA-1 进行签名,尽管它存在已知的 密码学弱点,但这样做是为了与较旧的订阅者互操作。
当订阅者接收到指定了 X-Hub-Signature 标头的内容分发请求时, 它们SHOULD 使用共享秘密值,并使用与中心相同的方法 (在 X-Hub-Signature 标头中提供)重新计算签名。如果签名 不匹配,订阅者MUST 在本地将该消息作为无效消息忽略。 订阅者MAY 仍然以 2xx 响应码确认此请求, 以便能够异步处理消息和/或防止对 签名的暴力尝试。将此技术与用于订阅请求的 HTTPS [RFC2818] 结合使用, 可使简单订阅者从中心接收经认证的内容分发请求,而无需 订阅者运行 HTTPS [RFC2818] 服务器。
不过请注意,该签名仅确保负载未被伪造。由于 请求还包含标头,除非订阅者使用 HTTPS [RFC2818] 回调, 否则订阅者不应将这些标头视为安全。
以下是安全注意事项的摘要。需要注意的是,WebSub 是一种服务器到服务器 协议,仅依赖 HTTP。强烈建议对所有请求使用 HTTPS。
订阅者是否应在页面的 <body> 内部(以及 <head> 内部)查找 <link> 元素,这一决定并不简单, 目前也没有明确共识。在发现期间忽略 <body> 的一个原因是, 某些网站可能(也许是意外地)允许用户发布包含 <link> 元素的内容,尽管工作组不知道任何此类网站的具体示例。 如果 WebSub 发现使用这样的 <link> 元素,那么向 此类网站贡献内容的用户可能会恶意地使所有订阅者使用备用中心,而该中心之后 递送恶意内容。鉴于这种潜在攻击,只在 HTML 文档的 <head> 中执行发现可能更为谨慎。
首先,订阅者SHOULD 始终优先使用中心的 HTTPS URL(即使该 URL 被通告为 HTTP)。其次,订阅者SHOULD 为回调使用唯一且不可猜测的 capability URL,并使它们可通过 HTTPS 访问。最后,订阅者SHOULD 在订阅时使用 hub.secret,以允许对 内容分发进行签名。
中心SHOULD 强制执行短期的 hub.lease_seconds(10 天是一个 良好的默认值)。执行意图验证时,中心SHOULD 使用 随机的一次性 hub.challenge。
意图验证过程会引入反射式代码注入(反射式 XSS)攻击的风险。
订阅者MUST 使用安全媒体类型
(例如 application/octet-stream)提供意图验证响应,
以防止此类攻击。此外,意图验证响应可能被利用来绕过
Content-Security-Policy
限制:订阅者MUST 以
X-Content-Type-Options: nosniff 提供意图验证响应,以缓解这种风险。
意图验证过程可能被用于欺骗用户从受信任的订阅者下载恶意资源。
订阅者SHOULD 对
hub.challenge 参数施加限制,例如限制其长度并拒绝二进制数据。
中心MUST 使用订阅者所使用的确切回调(包括使用 HTTPS)。如果被请求,中心MUST 使用订阅者提供的 hub.secret 对其请求进行签名。
如果订阅者在订阅请求中包含了 hub.secret,则订阅者SHOULD 验证中心提供的签名;如果它们这样做,则它们MUST 使用服务器所声明的签名机制,并丢弃 未通过测试的请求。
如果订阅者未使用安全回调 URL(HTTPS),或者怀疑中心与订阅者之间的 TLS 传输 可能已被破坏,那么内容递送 通知的完整性仅由 hub.secret 和所使用的散列算法保护。在这种 情况下,应根据应用程序的安全要求使用适当的散列算法。 由于截至本文档发布日期 SHA-1 已被证明遭到破坏,因此 至少应使用 SHA-256。
这些问题提供了本规范的安全与隐私注意事项概览,其依据是 Self-Review Questionnaire: Security and Privacy([security-privacy-questionnaire])的指导。
本节是非规范性的。
编辑感谢 PubSubHubbub 的作者、IndieWeb 社区以及其他实现者的 支持、鼓励和热情。特别是,编辑感谢 Brad Fitzpatrick、 Brett Slatkin、 Martin Atkins、 Amy Guy、 Barry Frost、 Benjamin Roberts、 Eugen Rochko、 Jordan Potter、 Matthias Pfefferle、 Malcolm Blaney、 Marten de Vries、 Sandro Hawke、 Tantek Çelik、 以及 Tony Garnock-Jones。
W3C 团队感谢 Gabriel Corona 报告 XSS
本节是非规范性的。
.host-meta 发现特性(Issue
#97)hub.secret 应是加密随机且
唯一的Accept-Language
标头返回不同 rel=self URL 的示例<link> 标签rel=self URL 支持内容协商的示例hub.topic 必须是已发现的 self URLFrom 标头的建议