RSS加入WebSub Hub後無法推播的原因:被Cloudflare防火牆阻擋
通常有些網站內容需要即時性通知(例如新聞網站),建議將RSS加入WebSub宣告,因為它是主動推播,而一般RSS是傳統輪詢。可以看一下這篇文章的解說:〈How WebSub works: and why it’s good for your podcast〉。
但是網站有用Cloudflare服務的話,WebSub Hub極有可能被其防火牆阻擋而無法推播。
注意
本文由 Claude Sonnet 4.6 語言模型產生。
前言
WebSub(前身為 PubSubHubbub)是一個讓網站能「主動推播」內容更新給訂閱者的協定,常見應用包含 RSS to Email 服務(如 Blogtrottr)、RSS 閱讀器的即時更新(如 Feedly、Inoreader、FreshRSS),以及加速 Google 搜尋收錄。
對靜態網站來說,實作 WebSub 的標準流程是:
- 在 RSS feed 裡宣告使用的 Hub(如 Google Hub 或 Superfeedr)。
- 在每次部署後,透過 CI/CD(如 GitHub Actions)ping Hub 通知「內容已更新」。
這個流程看起來簡單,但有一個很容易被忽略的問題,導致設定完全正確、ping 也成功,Hub 卻無法推播。
問題描述
症狀:
- GitHub Actions ping Hub 回傳
HTTP 204(表示 Hub 成功收到通知)。 - 但 RSS 訂閱者(如 Blogtrottr)收到更新的時間仍然很慢,沒有明顯改善。
- 到 Google Hub 的 Topic Details 頁面查看,發現 Last successful fetch 停在舊的時間點
- Google Hub 甚至出現
HTTP 504的 fetch error。
WebSub 的完整流程如下:
你發文
↓
CI/CD ping Hub(你告訴 Hub「我更新了」)← ping 成功 ✅
↓
Hub 去抓你的 rss.xml(Hub 確認新內容)← 504 失敗 ❌
↓
Hub 推播給訂閱者 ← 永遠不會發生
ping 成功只是第一步,Hub 收到通知後,還需要自己去抓你的 feed 才能確認新內容。如果這個抓取動作被擋下來,整個推播流程就斷掉了。
根本原因:Cloudflare 默默丟棄 Hub 的抓取請求
使用 Cloudflare 的網站,預設的 Security Level 或相關機制會對「可疑的 Bot 流量」發出 JavaScript Challenge。
Hub 的爬蟲是純 HTTP 客戶端,無法解 JavaScript Challenge,結果就是:
- 請求超時 → 回傳
504。 - 這個過程不會在 WAF Log 留下記錄(因為被更前面的層級擋住了),所以 WAF Log 顯示 0,讓人誤以為沒有任何請求被擋。
Google Hub 遇到 504 後,會啟動指數退避(Exponential Backoff)機制──即使之後的 ping 都正常回傳 204,Hub 內部對這個 feed 的抓取排程已經被推遲很久,可能是數小時到數天,導致推播長期失效。
解法:在 Cloudflare 對 /rss.xml 建立 WAF 跳過規則
在 Cloudflare Dashboard → 網路安全 → WAF → 自訂規則,新增一條規則:
| 欄位 | 設定 |
|---|---|
| 規則名稱 | Allow RSS Feed Crawlers |
| 編輯運算式 | http.request.uri.path eq "/rss.xml" |
| 選擇動作 | Skip |
| 要跳過的 WAF 元件 | 所有受控規則 + 安全等級(Security Level) |
這樣所有對 /rss.xml 的請求都會跳過安全檢查,Hub 的爬蟲就能正常抓取 feed。
設定完成後,WAF Log 就會開始出現 採取的動作:跳過 的記錄,確認規則有命中。
驗證方式
確認 Hub 能正常抓取:
到 Google Hub 的 Topic Details 頁面查看:
https://pubsubhubbub.appspot.com/topic-details?hub.url=https://yoursite.com/rss.xml
Last successful fetch 的時間應該會在你 ping 之後更新。
確認推播正常:
發文後,RSS to Email 服務(如 Blogtrottr)應在數分鐘內收到信,而非半小時甚至更久。
附:靜態網站實作 WebSub 的完整設定
RSS feed 宣告 Hub(以 Astro 為例)
// src/pages/rss.xml.js
return rss({
// ...
xmlns: { atom: 'http://www.w3.org/2005/Atom' },
customData: `
<language>zh-TW</language>
<atom:link href="${feedUrl}" rel="self" type="application/rss+xml" />
<atom:link href="https://pubsubhubbub.appspot.com/" rel="hub" />
`,
});
HTML <head> 也宣告 Hub(可選但建議)
根據 W3C WebSub 規格,訂閱者做 discovery 時的順序是:HTTP Link Header → HTML <head> → RSS feed 內的 <link>。
RSS feed 裡的宣告對 Blogtrottr 這類直接訂閱 feed URL 的服務已經夠用,但如果有工具是從網頁 URL 開始做 discovery,就需要 HTML <head> 裡的宣告才能找到 Hub。加上這行可以讓設定更完整:
<!-- HTML <head> 裡,例如 Layout.astro -->
<link rel="hub" href="https://pubsubhubbub.appspot.com/">
GitHub Actions 在部署後 ping Hub
- name: Ping WebSub Hub
run: |
curl -s -X POST "https://pubsubhubbub.appspot.com/" \
-d "hub.mode=publish" \
-d "hub.url=https://yoursite.com/rss.xml" \
-w "\nWebSub Ping HTTP Status: %{http_code}\n"
HTTP 204 代表 Hub 成功收到通知。
防止 Cloudflare Edge Cache 導致 504
在 public/_headers 設定(Cloudflare Pages):
/rss.xml
Cache-Control: no-cache, no-store, must-revalidate, max-age=0
或在 Cloudflare Dashboard 的 Cache Rules 加一條 Bypass Cache 規則,確保每次請求都拿到最新的 feed 內容。
總結
| 現象 | 原因 |
|---|---|
| ping 回傳 204,但 Hub 不推播 | Hub 的抓取請求被 Cloudflare 擋住(504) |
| WAF Log 顯示 0 筆記錄 | 請求在 WAF 之前的層級就被默默丟棄 |
| 前幾天正常,之後突然失效 | 一次 504 觸發 Hub 的指數退避機制 |
| 加了 WAF 跳過規則後恢復正常 | Hub 爬蟲終於能正常抓取 feed |
結論:WebSub 的 ping 成功只是第一步,Hub 能不能抓到你的 feed 才是關鍵。使用 Cloudflare 的網站,記得對 RSS feed 路徑加上 WAF 跳過規則。