跳到主要內容

【2026 最新】Accept Header Markdown 怎麼做?WordPress/Cloudflare 6 組 curl 實作

最後更新: ·
Accept Header Markdown 教學首圖

你在瀏覽器打開文章時,需要導覽列、字型與互動元件;AI Agent 抓同一頁時,真正需要的通常是標題、段落、程式碼與連結。Accept Header Markdown 的做法,就是讓同一個網址依請求偏好回傳 HTML 或乾淨 Markdown,而不是維護兩套內容。

這個問題在 2026 年 8 月 26 日登上 Hacker News;截至 8 月 28 日,該討論串顯示 172 points、103 comments。熱度背後真正難的不是「把 HTML 轉成 Markdown」,而是 q-value、406Vary: Accept 與 CDN cache key 要一起正確。

這篇專為第一次碰 HTTP 協定與網站快取的讀者寫:你會先理解一個白話模型,再完成 WordPress、Cloudflare 原生功能或 Worker 三條路線,最後用六組 curl 驗收。看完不只知道怎麼開功能,也知道怎麼避免「瀏覽器突然收到 Markdown」這種快取事故。

先說結論:同一門牌,兩份餐點

一句話記住:同一網址 + Accept 偏好 = HTML/Markdown 兩種表示。

把網址想成餐廳門牌,Accept 是客人點餐,Content-Type 是廚房實際端出的餐點,Vary: Accept 則是貼給快取看的分流牌。門牌不變,內容的「表示方式」可以不同;文章本身仍只有一份。

  1. 公開網址直接由 WordPress render:可評估 Roots 外掛,但 v1.7.1 要先加本文的內容保護閘門。
  2. 網站已在 Cloudflare 且方案符合:先用 Markdown for Agents,最少改動。
  3. 你已有靜態 .md 檔、要自訂嚴格度:再選 Cloudflare Worker。

三條路只選一個主要 Markdown 產生器。若 WordPress 與 Cloudflare 同時各自轉換,除錯時很難判斷 body、header 與 token 計數到底出自哪一層。若 WordPress 只是後台、公開網址由 Next.js 等 headless frontend 回應,裝在 CMS 的外掛不會改變公開網址;協商必須做在 frontend 或 edge。

Accept Header Markdown 是什麼?先把協商說白

RFC 7763 登記了 text/markdown 媒體類型;RFC 9110 則定義 HTTP 內容協商。Agent 可以這樣說:「我最想要 Markdown,也能退回 HTML。」

Accept: text/markdown;q=1, text/html;q=0.5

q 是 0 到 1 的偏好權重;省略時預設為 1,q=0 代表該表示不可接受。伺服器選好後,回應應用 Content-Type 告訴客戶實際格式:

Content-Type: text/markdown; charset=utf-8
Vary: Accept
同一網址依 Accept Header 分流 HTML 與 Markdown,並形成兩個快取變體的流程圖
一個網址、兩種表示;Accept 負責選擇,Vary 負責提醒快取分流。

q-value 的完整走法

假設客戶送出:

Accept: text/markdown;q=0, text/*;q=0.8, text/html;q=1
  1. text/html 有精確規則,品質為 1。
  2. text/markdown 也有更精確規則,品質為 0;不能拿較寬鬆的 text/*;q=0.8 蓋過它。
  3. 因此應回 HTML。若 HTML 與 Markdown 都被設為 q=0,伺服器可回 406 Not Acceptable;RFC 9110 也允許伺服器忽略偏好、回預設表示。

白話說:先看哪條規則最精確,再看那條規則的 q-value。正式環境不要只用「字串裡有沒有 text/markdown」判斷,否則 q=0、萬用字元與多值 header 都會出錯。

為什麼不能漏掉 Vary 與 CDN cache key?

如果 CDN 只把 URL 當成鑰匙,第一個 Agent 把 Markdown 存進快取,下一個瀏覽器可能就會拿到同一份 Markdown。RFC 9111 規定快取重用含 Vary 的回應時,要比對被點名的請求 header;所以協商端至少要在 HTML 與 Markdown 回應都送出 Vary: Accept

Vary 是分流訊號,不等於每個 CDN 都會自動把所有任意 header 放進 cache key。以 Cloudflare 為例,目前的 Vary 文件要求來源回應先包含 Vary,再透過 Cache Rules Vary 或 Worker 的 cf.vary 指定如何處理。只協商 text/htmltext/markdown 時,normalize 能避免每種無關的 header 排列都製造新變體;若你還依賴 variant 等媒體參數,Cloudflare 的正規化會移除參數,應改用 passthrough 或重新設計 cache 維度。

cf: {
  vary: {
    default: { action: "bypass" },
    headers: {
      accept: {
        action: "normalize",
        media_types: ["text/html", "text/markdown"]
      }
    }
  }
}

這是 Worker 子請求的 cf.vary 片段;來源仍要回 Vary: Accept。修改 Vary 設定後,Cloudflare 文件也明確指出既有快取不會跟著自動清除,因此上線切換時要為受影響 URL 做一次精準 purge,再重新暖兩個變體。

路線一:Accept Header Markdown 用在 WordPress

如果公開文章直接由 WordPress render,Roots 的開源 Post Content to Markdown 可處理 Accept.md 後綴與 ?format=markdown。截至 2026 年 8 月 28 日,GitHub 最新 release 為 1.7.1,外掛標示需要 PHP 8.1 以上。

先說安全結論:我們檢查 v1.7.1 原始碼時,沒有看到 post_password_required() 的明確檢查;單篇路徑直接讀取 $post->post_content,Markdown feed 也另有自己的輸出流程。因此密碼保護或非公開內容網站,不應原樣啟用。下面先把單篇範圍鎖成「已公開且沒有文章密碼」,並暫停 Markdown feed。

步驟 1:安裝並只開在測試環境

composer require roots/post-content-to-markdown

沒有 Composer 的網站,可從 GitHub Releases 下載 zip,放到 wp-content/plugins/post-content-to-markdown/ 後啟用。先在 staging 跑完本文六組驗收,再推到正式站。

步驟 2:先加內容保護閘門

把以下內容放進自己的 mu-plugin;它利用外掛公開的 post_allowed filter 鎖住單篇文章,並在更早的 template_redirect 優先序擋掉所有 Markdown feed。這是針對目前 v1.7.1 的 fail-closed 配置,升級外掛後要重新做整合測試。

<?php

add_filter(
  'post_content_to_markdown/post_allowed',
  function ($allowed, $post) {
    return $allowed
      && $post->post_status === 'publish'
      && empty($post->post_password);
  },
  10,
  2
);

add_filter(
  'post_content_to_markdown/feed_post_types',
  '__return_empty_array'
);

add_action('template_redirect', function () {
  if (! is_feed()) {
    return;
  }

  $accept = strtolower($_SERVER['HTTP_ACCEPT'] ?? '');
  $is_markdown_feed = get_query_var('feed') === 'markdown'
    || str_contains($accept, 'text/markdown');

  if (! $is_markdown_feed) {
    return;
  }

  status_header(406);
  header('Content-Type: text/plain; charset=utf-8');
  header('Cache-Control: no-store');
  header('Vary: Accept', false);
  exit("Markdown feeds disabled\n");
}, 0);

這段對 feed 採保守策略:只要 header 提到 Markdown 就擋,包括 q=0。它犧牲少量相容性來避免 feed 走進未套用單篇 post_allowed 的路徑。若業務真的需要 Markdown feed,應另寫逐篇權限檢查與密碼文章整合測試,再移除此擋板。

步驟 3:先驗證三個入口

curl -sS -D - -o /dev/null \
  -H 'Accept: text/markdown' https://example.com/post-slug/

curl -sS -D - -o /dev/null \
  https://example.com/post-slug.md

curl -sS -D - -o /dev/null \
  'https://example.com/post-slug/?format=markdown'

第一個是本文主角;後兩個是容易分享與除錯的顯式入口。外掛目前會為 Markdown 回應送出 Content-Type: text/markdownVary: AcceptX-Markdown-Source,也會在 HTML 回應宣告 .md alternate。這讓你能從存取紀錄分辨請求是從 header、後綴或 query 進來。

步驟 4:決定 406 策略

外掛預設為 strict:若客戶明確排除 HTML 與 Markdown,就回 406。如果你的舊爬蟲把奇怪的 Accept 當慣例、你想保留 HTML fallback,可以用官方 README 提供的 filter:

add_filter(
  'post_content_to_markdown/strict_accept',
  '__return_false'
);

這不是「哪個比較符合規範」的選擇,而是 API 契約:strict 適合早點暴露錯誤,fallback 適合相容性優先。選定後把它寫進測試,別讓環境之間漂移。

步驟 5:分清 WordPress page cache 與 CDN cache

此外掛預設對 Markdown 請求設定 DONOTCACHEPAGE,降低 WordPress 頁面快取交叉送錯格式的風險。這不會替外層 CDN 完成 cache-key 設定;若前面還有 Cloudflare、Fastly 或其他反向代理,仍要逐層驗證。

路線二:在 Cloudflare 直接產生 Markdown

A. 最少改動:Markdown for Agents

Cloudflare 的 Markdown for Agents 目前標示為 Beta,可在邊緣把 HTML 轉成 Markdown。到 Dashboard 的 AI Crawl Control → Markdown for Agents 開啟後,帶有 Accept: text/markdown 的合格請求會收到 Markdown;也能用 Configuration Rule 只套到特定 hostname 或 path。

截至 2026 年 8 月 28 日,官方頁面列出的可用範圍是 Pro、Business、Enterprise 與 SSL for SaaS;輸入是 HTML,來源回應上限 2 MB。轉換回應會包含 x-markdown-tokensx-original-tokens 估計值,並調整 Content-TypeContent-LengthVary 等 header。這條路適合「來源不想改、先在 edge 做轉換」的網站。

官方文件保證的是「轉換後回應」會讓 Vary 包含 Accept,沒有把完整 q-value tie-break、406 或所有 HTML fallback header 寫成介面契約。因此仍要在自己的 zone 跑本文六組測試,並確認 HTML 與 Markdown 兩邊都具備正確分流訊號;不要把 Cloudflare 文件頁今天的觀察行為當成永久保證。

若來源有內容使用政策,還要檢查 Content-Signal 是否如預期保留。Cloudflare 文件說明:來源未提供時,產生的 Markdown 會帶入其預設的 yes 值;這屬於內容授權訊號,不是格式轉換細節。此外,轉換會移除導覽、頁首、頁尾、script 與 style;若重要歸因、授權文字或產品條件放在這些區域,必須把它們列入內容完整度驗收。

B. 控制力優先:Worker + 靜態 .md 兄弟檔

若你的 build pipeline 能為每頁產生 index.htmlindex.md,Worker 只需負責協商與取檔,不必在每次請求重新轉換。Accept Markdown 的 Worker recipe 可當成靜態資產 binding、406 與 alternate link 的部署骨架;其中 parser 適合示範常見 header,不是完整 RFC parser。若你允許帶引號的參數、variant 或更複雜的 media range,應換成經測試的解析器。核心設定是讓 Worker 先接住請求:

[assets]
directory = "./public"
binding = "ASSETS"
run_worker_first = ["*"]
  1. 解析 Accept,以精確度與 q-value 算出 HTML/Markdown 偏好。
  2. 選 Markdown 時,把 /guide/ 對應到 build 產生的 /guide/index.md
  3. 回應 text/markdown; charset=utf-8Vary: Accept
  4. 用 Cache Rules Vary 或 cf.vary 把兩種表示分開快取。

不要把 parser 縮成一行 accept.includes('text/markdown');那會把 q=0 當成同意,也忽略 HTML 的較高偏好。最少要把本文六組案例、精確拒絕覆蓋 wildcard,以及你允許的參數語法寫成自動測試。想理解 Agent 如何在這類伺服器端流程裡拿工具與資料,可以先讀 AI Agent Harness 是什麼,再接著看 如何實作 Agent Harness

六組 curl:上線前一次驗完

URL 換成同一篇公開文章。每組都同時看 status、Content-TypeVary 與 body;只看 HTTP 200 不算通過。

URL='https://example.com/post-slug/'

# 1. 明確要 Markdown
curl -sS -D - -o /tmp/md.body \
  -H 'Accept: text/markdown' "$URL"

# 2. 明確要 HTML
curl -sS -D - -o /tmp/html.body \
  -H 'Accept: text/html' "$URL"

# 3. HTML 權重較高
curl -sS -D - -o /dev/null \
  -H 'Accept: text/markdown;q=0.2, text/html;q=0.9' "$URL"

# 4. Markdown 被明確拒絕
curl -sS -D - -o /dev/null \
  -H 'Accept: text/markdown;q=0, text/html;q=1' "$URL"

# 5. 精確拒絕要蓋過較寬的 wildcard:不得回 Markdown
curl -sS -D - -o /dev/null \
  -H 'Accept: text/markdown;q=0, text/*;q=1' "$URL"

# 6. 兩種表示都被拒絕:依你的契約驗 406 或 fallback
curl -sS -D - -o /dev/null \
  -H 'Accept: text/markdown;q=0, text/html;q=0' "$URL"

再做最重要的交錯測試:連續送「Markdown → HTML → Markdown」,每次記錄 AgeCF-Cache-Status 或你的 CDN cache header,確認命中後仍各自得到正確格式。單次 MISS 正確,不代表 HIT 也正確。

量測:bytes、token 與引用完整度怎麼看?

我們在 2026 年 8 月 28 日保留了一份可重跑的單頁量測:同一個 Cloudflare 官方 Markdown for Agents 文件 URL,使用 GET 與 curl --compressed,將解壓後 body 寫入檔案再以 wc -c 計算。

2026 年 8 月 28 日 Cloudflare 單頁 HTML 與 Markdown bytes、token header 估計及內容連結保存比較
單頁量測快照:解壓後 body 縮小 90.8%;token 數字來自 Cloudflare 回應 header,不能外推成所有網站的成本比例。
  • 解壓後 body:HTML 186,385 bytes;Markdown 17,190 bytes,這一頁減少 90.8%。
  • Cloudflare header 估計:x-original-tokens: 46485x-markdown-tokens: 4289
  • 內容連結抽查:預先選定內文中的 MDN content negotiation、Cloudflare 公告、Content Signals 與 Workers AI 轉換說明,Markdown 保留 4/4。

這組數字只描述該 URL、該日回應與該量測方法,不是跨網站 benchmark。真正要追的是三個分開的指標:

  1. 傳輸 bytes:用相同 URL、method、壓縮設定與時間窗比較。
  2. 模型輸入 token:固定 tokenizer 或記錄供應商 header;不要用「字元除以四」估中文。
  3. 內容完整度:先選標題、程式碼、圖片 alt、JSON-LD 或幾條重要連結,再逐項比對;總連結數下降可能只是導覽列被正確移除。

這和 Claude Code 讀 HTML 還是 Markdown 的檔案格式比較不同:本篇處理的是 HTTP 傳輸層。若你想把「內容有沒有真的更常被引用」也納入驗收,可接著用 GEO AI 引用 A/B Test 的方法做獨立實驗,不要把 payload 變小直接等同於排名提升。

最常踩的 7 個坑

  1. 只在 Markdown 回應加 Vary:HTML 先被快取時仍可能交叉污染;兩邊都要帶。
  2. 把 Vary 當成 CDN 設定本身:逐層確認 cache key,而不是只看 origin header。
  3. 用 includes 解析 Accept:q=0、萬用字元與較高 HTML 偏好都會判錯。
  4. 同時開兩個轉換器:先選 WordPress、Cloudflare managed 或 Worker 其中一層當 owner。
  5. 只測 MISS:一定要交錯測到 CDN HIT,才能抓出變體共用。
  6. 只數 token:若標題層級、code fence、重要連結或結構化資料遺失,body 再小也不合格。
  7. 假設每個 Agent 都會送同一種 header:截至 2026 年 8 月,Accept Markdown 專案的觀察矩陣顯示不同客戶端行為不一;把自己網站的 access log 當成採用依據。

FAQ:Accept Header Markdown 常見問題

1. 同一網址回兩種內容,會產生重複內容嗎?

不是兩篇文章。這是同一資源的兩種表示。若另外提供 .md URL,應保留 HTML canonical,並像 Roots 外掛那樣為 Markdown alias 設定適當的 robots 與 alternate 關係。

2. 一定要回 406 嗎?

不一定。RFC 9110 允許伺服器回 406,也允許忽略偏好並回預設表示。重點是把策略寫進契約與測試,不要讓不同環境隨機選擇。

3. 有 Vary: Accept 就完成了嗎?

還差 CDN 驗收。Vary 是必要訊號;你的 CDN 是否把它納入 cache key、如何 normalize,仍要看產品設定並用 HIT 交錯測試證明。

4. WordPress 外掛和 Cloudflare 可以一起開嗎?

技術上可串接,營運上先指定唯一 owner。若 WordPress 已回 Markdown,就讓 Cloudflare只做傳遞與正確快取;若 Cloudflare負責轉換,來源保持 HTML。這樣 header、body 與錯誤都能追到一層。

5. Markdown 一定比較省 token 嗎?

要量。移除導覽、樣式與腳本通常會縮小輸入,但比例取決於頁面、轉換器與 tokenizer。本篇 90.8% 是一頁的 dated snapshot,不是通用係數。

6. 這會直接提升 AI 引用或 SEO 嗎?

不能從格式直接推論。它改善的是內容取得介面;抓取成功率、引用完整度、搜尋表現與商業結果要各自設計觀測或實驗。

7. 為什麼不用獨立的 /article.md?

兩種都能用。同 URL 協商讓 canonical 與內容身分一致;.md 兄弟 URL 比較容易分享、除錯與使用一般 URL cache。Roots 外掛同時提供兩者,Worker 也可用 alternate link 宣告。

8. 怎麼知道真的有 Agent 在用?

看伺服器紀錄,不看想像。記錄協商結果、Accept 正規化後的類別、status、cache status、bytes 與 user agent;若用 Roots 外掛,也可記錄 X-Markdown-Source。先建立基準,再決定是否擴大覆蓋。

給新手的 5 個上線重點

  1. 先選唯一 Markdown 產生層,不要讓 WordPress 與 edge 搶 owner。
  2. 用經測試、符合你接受語法範圍的 parser;q=0 必須真的代表拒絕。
  3. HTML、Markdown 都送 Vary: Accept,並配置 CDN cache key。
  4. 六組 curl 加上 HIT 交錯測試,一起驗 status、header、body。
  5. bytes、token、內容完整度分開量;不把單頁結果外推成排名或成本承諾。

如果這是你第一次做 Agent 基礎設施,推薦先補上 Claude 省 token 的系統方法,再比較 Claude Code 與 Codex 的工作流。想把零散技巧整理成完整能力,也可以從 AlphaLab 的 AI 課程繼續學。

接著閱讀

左右滑動查看更多推薦

結語:先讓一個 URL 正確,再擴到全站

回到本文的記憶句:同一網址 + Accept 偏好 = HTML/Markdown 兩種表示。真正把它變成可營運功能的,是後半句:Content-Type 說明端了什麼,Vary 與 cache key 確保別送錯桌。

現在就挑一篇 staging 文章,只開一個 Markdown 產生器,跑完六組 curl 與 HIT 交錯測試。header、body、快取三關都過,再逐步擴到整站;這才是 Agent 友善網站最小、可回滾的起點。

ALPHALAB 社群

有問題?來 Telegram 聊

和 Terry、編輯、其他網友一起討論這篇文章。提問、分享觀點,回覆更即時。

加入 Telegram 討論

📩 訂閱 AlphaLab 電子報

每週最多三封:一封 Weekly 週報與最多兩封關鍵 Alpha Signal。

我們不會 spam,隨時可退訂。