Hugo 靜態網站社群預覽完整踩坑紀錄:og:image、Cloudflare、Facebook 爬蟲 403
把文章分享到社群平台,結果跳出「No title」、「No description」,或是預覽圖莫名出現頭像——這篇文章完整記錄這些問題的根本原因,以及一個一個解掉的過程。
背景
在完成 Hugo 靜態網站 SEO + AEO 最佳化之後,實際把文章貼到 Buffer 準備同步發佈到 Facebook、LinkedIn 和 X,結果馬上踩到一系列坑。
問題一:Buffer 顯示「No title、No description」
症狀
在 Buffer 貼入文章 URL,預覽顯示:
No title
blog.example.com
No description
Buffer 錯誤訊息:
We were unable to generate a preview of this link. This may happen if the website blocks Buffer’s access to images and other content.
排查過程
第一反應是 og:title 和 og:description 沒有輸出。用 curl 模擬 Facebook 爬蟲:
curl -s -A "facebookexternalhit/1.1" https://blog.example.com/posts/your-post/
HTML 內容完全正確,og 標籤都在。問題不在程式碼。
接著測試 robots.txt:
curl -s https://blog.example.com/robots.txt
回傳的不是自己寫的內容,而是這個:
# As a condition of accessing this website, you agree to abide by the following
# content signals:
# search: building a search index...
# ai-input: inputting content into one or more AI models...
# ai-train: training or fine-tuning AI models.
根本原因
Cloudflare 的 Managed robots.txt 功能開著,它會攔截對 /robots.txt 的請求,用 Cloudflare 自己的「Content Signals Framework」格式回應,完全取代你放在 static/robots.txt 的檔案。
Buffer 的爬蟲讀到這個格式不認識的 robots.txt,可能誤判為被封鎖,導致放棄抓取 metadata。
解法
Cloudflare Dashboard → Scrape Shield → Managed robots.txt → 關閉
關閉後 robots.txt 恢復正常。
問題二:Facebook Sharing Debugger 回傳 403
症狀
到 Facebook Sharing Debugger (developers.facebook.com/tools/debug/) 貼入 URL 重新抓取,結果:
回應碼:403
回應碼原因:This response code could be due to a robots.txt block.
Please allowlist facebookexternalhit on your sites robots.txt config.
排查過程
明明 robots.txt 已經是 User-agent: * Allow: /,照理說 Facebook 爬蟲不應該被擋。
用 curl 模擬確認:
curl -s -o /dev/null -w "%{http_code}" \
-A "facebookexternalhit/1.1" \
https://blog.example.com/posts/your-post/
# 回傳:200
本機模擬是 200,但 Facebook 真實爬蟲拿到 403。
根本原因
Cloudflare 的 Bot Fight Mode 把 Facebook 真實爬蟲的 IP 當成惡意 bot,在網路層直接封鎖,請求根本到不了 origin server,更不會去看 robots.txt。
這就是為什麼:
- 本機用相同 User-Agent curl 是 200(本機 IP 沒被 Cloudflare 標記)
- Facebook 真實爬蟲是 403(Facebook 的 IP 被 Bot Fight Mode 封鎖)
解法(選一)
方案 A:直接關閉 Bot Fight Mode(適合靜態網站,攻擊面小)
Cloudflare Dashboard → Security → Bots → Bot Fight Mode → 關閉
靜態 HTML 網站基本上沒什麼可攻擊的(無資料庫、無後端程式、無登入),Bot Fight Mode 帶來的保護效益有限,關掉換取社群爬蟲正常運作是合理的取捨。
方案 B:建立 WAF Custom Rule 精準放行(保留 Bot Fight Mode)
Cloudflare Dashboard → Security → WAF → Custom Rules → Create rule
在「Edit expression」模式貼入以下表達式,Action 選 Skip → Skip all remaining custom rules:
(http.user_agent contains "facebookexternalhit") or
(http.user_agent contains "Twitterbot") or
(http.user_agent contains "LinkedInBot") or
(http.user_agent contains "Bufferbot") or
(http.user_agent contains "PerplexityBot") or
(http.user_agent contains "GPTBot") or
(http.user_agent contains "ClaudeBot")
問題三:沒有設定 og:image,頭像卻自動出現在預覽圖
症狀
文章 front matter 沒有設定 image,程式碼也沒有輸出 og:image meta tag,但 Facebook、Buffer 的預覽圖卻顯示網站頭像。
確認 meta tag 確實不存在:
curl -s https://blog.example.com/posts/your-post/ \
| grep 'property="og:image"\|property=og:image'
# 沒有輸出 → meta tag 確實不存在
根本原因
Facebook 有 fallback 機制:當頁面沒有 og:image meta tag 時,Facebook 爬蟲會自動掃描頁面裡所有的 <img> 標籤,抓第一張圖作為預覽圖。
確認頁面裡的 img 標籤:
curl -s https://blog.example.com/posts/your-post/ \
| grep -o 'img[^>]*src=[^[:space:]>]*'
# 輸出:img src=/images/avatar.jpg
Sidebar 的作者頭像用 <img> 標籤載入,Facebook 掃到了就拿去用。
解法
把 sidebar 頭像從 <img> 改成 CSS background-image。Facebook 爬蟲只掃 <img> 標籤,不解析 CSS,所以改用 CSS 載入之後,Facebook 就掃不到這張圖。
Layout 改動(layouts/partials/header.html):
<!-- 改前 -->
<img src="/images/avatar.jpg" alt="作者名稱" class="sidebar-avatar" />
<!-- 改後 -->
<span class="sidebar-avatar" role="img" aria-label="作者名稱"></span>
CSS 改動(assets/css/main.css):
/* 改前 */
.sidebar-avatar {
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
}
/* 改後 */
.sidebar-avatar {
width: 100px;
height: 100px;
border-radius: 50%;
display: inline-block;
background-image: url('/images/avatar.jpg');
background-size: cover;
background-position: center;
}
視覺上完全一樣,但 Facebook 爬蟲看不到頭像了。
改完後驗證:
curl -s https://blog.example.com/posts/your-post/ \
| grep -o 'img[^>]*src=[^[:space:]>]*'
# 沒有輸出 → 頁面裡已無任何 <img> 標籤
問題四:og:description 有設但預覽沒有文字
症狀
og:description 在 HTML 裡確認存在,但 Buffer 或 Facebook 預覽只有大標題,沒有描述文字。
根本原因
兩個可能:
1. 描述 fallback 到站點描述而非文章摘要
Hugo 模板常見寫法:
<!-- 錯誤:沒有 description front matter 時 fallback 到站點描述 -->
<meta property="og:description"
content="{{ with .Params.description }}{{ . }}{{ else }}{{ site.Params.description }}{{ end }}">
每篇文章分享出去都顯示一樣的站點 tagline,社群平台判定為重複內容可能降低展示優先度。
2. 快取問題
Facebook 快取了舊版 metadata,即使 HTML 已經更新,Facebook 仍顯示舊內容。
解法
修正 description fallback 邏輯,改成用文章摘要(<!--more--> 之前的內容):
{{ $desc := site.Params.description }}
{{ if .IsPage }}
{{ if .Params.description }}
{{ $desc = .Params.description }}
{{ else }}
{{ $desc = .Summary | plainify | truncate 160 }}
{{ end }}
{{ end }}
<meta name="description" content="{{ $desc }}">
<meta property="og:description" content="{{ $desc }}">
<meta name="twitter:description" content="{{ $desc }}">
優先順序:front matter description → 文章摘要前 160 字 → 站點描述(首頁用)
清除快取
Cloudflare Dashboard → Caching → Cache Purge → Custom Purge → 貼入 URL
清完後到 Facebook Sharing Debugger 再點一次「再次抓取」。
整體流程回顧
分享連結到 Buffer
↓
No title / No description
↓ 排查
Cloudflare Managed robots.txt 攔截 → 關閉
↓
Facebook Sharing Debugger 重抓 → 403
↓ 排查
Cloudflare Bot Fight Mode 封鎖 Facebook 爬蟲 IP → 關閉
↓
頭像出現在預覽圖(無 og:image 時 Facebook 掃 <img>)
↓ 排查
sidebar <img> 被掃到 → 改成 CSS background-image
↓
✅ 預覽正常:標題 + 文章摘要,無多餘圖片
小結
這次碰到的四個問題,表面上看起來像是「og 設定沒設好」,實際上大部分根因都在 Cloudflare 的預設行為:
| 問題 | 根因 | 一行解法 |
|---|---|---|
| No title / No description | Cloudflare Managed robots.txt | 關閉 Managed robots.txt |
| Facebook 爬蟲 403 | Cloudflare Bot Fight Mode | 關閉或 WAF 放行 |
| 頭像出現在預覽 | Facebook 掃 <img> fallback | 改 CSS background-image |
| Description 顯示站點描述 | Hugo fallback 邏輯錯誤 | Summary 優先於 site.Params.description |
對靜態網站來說,Cloudflare 帶來的保護主要是 CDN 加速和 L3/L4 DDoS 防護,應用層的 Bot Fight Mode 和 Managed robots.txt 反而會干擾正常的社群爬蟲和 AI 引擎,根據需求調整比全部開啟要好。