你把新文章部署上去,自己看得到最新版,讀者卻還在看昨天的內容?先別急著重跑 11ty,也別一杯接一杯咖啡地清快取。很多時候,問題不是建置失敗,而是瀏覽器、Hostinger、CDN 或 HTTP headers 還在提供舊檔案。

這篇會帶你用 11ty 建置 _site,再依檔案類型安排 HTML、CSS、JavaScript、圖片、字型和 Pagefind 搜尋資產的快取策略。你也會看到如何在 Hostinger hPanel 清除快取,以及用 curl 檢查公開網站實際回傳的 headers。快取不是壞掉的記憶體,只是它記得比你久了一點。

先記住快取原則

不要把所有檔案都快取一年。比較穩定的分法是:

  • HTML:短時間快取,或要求瀏覽器重新驗證,因為文章和首頁會更新。
  • CSS、JavaScript、圖片、字型:如果檔名包含版本號或內容指紋,可以長效快取。
  • Pagefind 搜尋資產:搜尋索引會隨文章更新,不能直接套用一般靜態資產的一年 TTL。

簡單說,內容頁要容易更新;URL 已經跟著內容改變的資產,才適合快取久一點。

11ty 與 Hostinger 各自負責什麼?

11ty 負責把 Markdown、模板和資產建置成 _site 裡的靜態檔案。它不會替你決定訪客的瀏覽器或 Hostinger CDN 要快取多久。

上線後的快取主要分成三層:

快取層級作用更新不生效時先檢查什麼
瀏覽器減少訪客重複下載檔案用無痕視窗測試
Hostinger 快取減少來源站重複處理請求hPanel 的 Clear cache 或 Cache Manager
CDN讓訪客從較近的節點取得檔案依 CDN 介面執行 Purge/Flush

所以,重新執行 11ty 只會更新輸出檔案,不能保證所有快取層立刻換成新版本。

部署前先確認 _site

在專案根目錄執行:

npx @11ty/eleventy

本專案的 pnpm run build 還會在 Eleventy 完成後執行 Pagefind,並寫入 _site/pagefind/。如果只是先檢查 Eleventy 輸出,可以執行上面的指令;正式部署則建議直接執行:

pnpm run build

接著確認 _site 裡有:

  • _site/index.html
  • 最新文章的 HTML
  • CSS 和 JavaScript
  • 圖片與字型
  • pagefind/ 搜尋資產(如果網站有使用 Pagefind)

上傳時,要把 _site 裡面的內容放到 Hostinger 的網站根目錄,常見位置是 public_html。不要把整個專案原始碼上傳到公開目錄,也不要多包一層 _site 資料夾。

依專案設定 public/.htaccess

本專案的 public/.htaccess 使用 Apache 的 mod_expiresmod_headersmod_setenvif 管理快取與 response headers。下面是與目前設定相同的重點片段;完整檔案還包含重新導向、安全標頭、Gzip 和目錄列表設定。

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresDefault "access plus 5 minutes"
  ExpiresByType text/html "access plus 3 minutes"
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType application/javascript "access plus 1 month"
  ExpiresByType application/x-javascript "access plus 1 month"
  ExpiresByType image/jpeg "access plus 1 month"
  ExpiresByType image/gif "access plus 1 month"
  ExpiresByType image/png "access plus 1 month"
  ExpiresByType image/webp "access plus 1 month"
  ExpiresByType image/svg+xml "access plus 1 month"
  ExpiresByType font/ttf "access plus 1 month"
  ExpiresByType font/otf "access plus 1 month"
  ExpiresByType font/woff "access plus 1 month"
  ExpiresByType font/woff2 "access plus 1 month"
</IfModule>

<IfModule mod_headers.c>
  <FilesMatch "\.(?:html?)$">
    Header set Cache-Control "public, max-age=180"
  </FilesMatch>
  <FilesMatch "\.(?:css|js|gif|jpe?g|png|webp|svg|ico|woff2?)$">
    Header set Cache-Control "public, max-age=31536000, immutable"
  </FilesMatch>
  Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</IfModule>

<IfModule mod_setenvif.c>
  SetEnvIfNoCase Request_URI "^/pagefind/" pagefind_asset
</IfModule>
<IfModule mod_headers.c>
  Header set Cache-Control "no-cache, must-revalidate" env=pagefind_asset
</IfModule>

這些設定分別做什麼?

  • ExpiresActive On:啟用 Apache 的過期時間設定。
  • ExpiresDefault "access plus 5 minutes":沒有特別指定類型的檔案,預設快取五分鐘。
  • ExpiresByType text/html "access plus 3 minutes":HTML 的 Expires 基準為三分鐘。
  • CSS、JavaScript、圖片和字型的 ExpiresByType:先設定為快取一個月,之後資產規則會再設定明確的 Cache-Control
  • Cache-Control: public, max-age=31536000, immutable:把符合檔名規則的 CSS、JavaScript、部分圖片、圖示與 woff 字型實際設定為快取一年。
  • SetEnvIfNoCase Request_URI "^/pagefind/" pagefind_asset:找出 /pagefind/ 開頭的檔案。
  • Header set Cache-Control "no-cache, must-revalidate" env=pagefind_asset:讓 Pagefind 更新後立即重新驗證。
  • Strict-Transport-Security:不是檔案快取,而是告訴瀏覽器未來一年都使用 HTTPS;目前也包含 includeSubDomains

一年快取有一個容易忽略的風險

目前的 <FilesMatch> 是按照副檔名套用一年快取,並沒有檢查檔名是否真的含有內容雜湊。因此沒有版本號的 style.cssapp.js 也會進入一年快取。如果這些檔案在原 URL 直接更新,訪客可能長時間拿到舊版本。

另一方面,mjsavifttfotf 雖然有一個月的 ExpiresByType,卻不符合目前的 <FilesMatch>,不會拿到一年期的 Cache-Control。要維持一年 TTL,部署流程就必須確保資產 URL 會隨內容改變,或另外為未版本化資產加上較短的規則。

為什麼 Pagefind 需要單獨設定?

Pagefind 的檔案可能同時符合一般 JavaScript 或資產檔案規則,因此先拿到一年的長效快取。接著,env=pagefind_asset 條件會再套用重新驗證的 Cache-Control

Cache-Control: no-cache, must-revalidate

這個例外很重要:一般資產可以快取一年,但搜尋索引不應該跟著使用同樣長的 TTL。

Header 需要 Apache mod_headersExpiresByType 需要 mod_expires,Pagefind 的環境變數需要 mod_setenvif。如果修改 .htaccess 後出現 500 錯誤,先還原修改,再逐段確認 Hostinger 是否支援相關模組與語法。

除了快取,.htaccess 還能負責哪些事情?

.htaccess 不只是快取設定檔,也可以集中管理 Apache 網站常見的伺服器規則。以下功能不一定全部都要啟用,應依網站需求和主機支援的 Apache 模組決定:

  • 靜態網站路由:使用 mod_rewrite 管理重新導向或找不到頁面的處理方式。11ty 已經產生檔案式路由時,不要直接套用 SPA 的「全部導向 /index.html」規則,否則原本的 404 和文章路徑可能一起被煮成一鍋。
  • 301 重新導向:將舊網址導向新網址,適合網站改版、文章改名或網址結構變更時使用。大量重定向最好由腳本產生,避免手動維護時遺漏或重複。
  • 安全標頭:可設定 X-Frame-OptionsX-Content-Type-Options 等 response headers,降低點擊劫持和 MIME 類型嗅探等風險。新增標頭前,應先確認不會影響網站嵌入或第三方服務。
  • Gzip 壓縮:使用 mod_deflate 壓縮 HTML、CSS、JavaScript 和 XML 等文字檔,減少傳輸量。JPEG、PNG、WebP 等通常已經壓縮,不需要再次壓縮。
  • 目錄保護Options -Indexes 可避免訪客直接看到沒有首頁檔案的資料夾列表。
  • HTTP 導向 HTTPS:使用 mod_rewrite 將 HTTP 請求重新導向 HTTPS。啟用前要先確認 SSL 憑證正常;HSTS 只會要求瀏覽器後續使用 HTTPS,不會代替 HTTP → HTTPS 的重新導向。

修改 .htaccess 前,先確認 Hostinger 支援所需的 Apache 模組與語法。建議一次只加入一個功能,修改後用 curl -I 和瀏覽器測試;若出現 500 錯誤,先還原最近一次修改,再逐段排查。

HSTS 不是檔案快取

這一行:

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

意思是瀏覽器在接下來一年內,連線到這個網站時優先使用 HTTPS。它不會決定 HTML、CSS 或圖片要存多久,因此不要把 HSTS 和 Cache-Control 當成同一種設定。

只有在 HTTPS 已經完全正常,而且所有會使用到的子網域也都能使用 HTTPS 時,才適合啟用較長的 HSTS 期限。

部署後到 hPanel 清除快取

每次部署後,依序做以下檢查:

  1. 進入 Hostinger hPanel 的網站 Dashboard,執行 Clear cache。不同方案或 hPanel 版本的選單名稱可能略有不同。
  2. 如果只想處理單一頁面,到 Advanced → Cache Manager 輸入 URL 後執行 Purge
  3. 如果網站啟用了 Hostinger CDN,到 Performance → CDN 檢查 CDN 狀態,並依介面提供的選項清除快取。
  4. 用無痕視窗重新開啟首頁和剛更新的文章。

不要一開始只清除自己的瀏覽器快取。如果所有訪客都看到舊版本,問題更可能在 Hostinger 快取、CDN 或部署檔案,而不是單一瀏覽器。

curl 驗收 response headers

檢查首頁:

curl -I https://example.com/

檢查一個版本化資產:

curl -I https://example.com/assets/app.8f31c.css

你可以觀察 Cache-ControlExpiresETagLast-ModifiedAgeContent-Type

依本專案目前設定,首頁通常會看到三分鐘的 max-age;符合資產副檔名規則的 CSS 則可能看到一年的 max-age。Pagefind URL 則應該要求重新驗證:

curl -I https://example.com/pagefind/pagefind.js

如果重新請求時內容沒有改變,伺服器可能回傳 304 Not Modified,代表條件式快取正常運作。若 curl 已經拿到新內容,但瀏覽器仍是舊內容,再用無痕視窗、另一個網路和 DevTools Network 交叉確認。

快取設定決策表

你的情況HTMLCSS/JS/圖片/字型建議
文章常更新,檔名沒有版本號no-cache 或短 TTL短 TTL,例如 5–30 分鐘先確保更新穩定,再逐步延長
CSS/JS 有內容指紋no-cache 或一天一年+immutable適合穩定的 production 靜態站
正在除錯快取問題no-cacheno-cache 或短 TTL驗收完成後再恢復長效快取
Pagefind 搜尋索引會更新短 TTLPagefind 使用 no-cache;其他資產依版本策略避免搜尋索引長時間未更新
CDN 剛啟用或剛清除短 TTL依 CDN 規則等待節點更新,再用不同網路測試

這張表主要根據兩個問題決定快取時間:檔案多久會更新,以及檔案的 URL 是否會隨內容改變。

  • HTML 通常使用短 TTL:首頁和文章內容可能經常更新,因此使用短時間快取,讓瀏覽器較快向伺服器確認。本專案目前設定為三分鐘;no-cache 則是另一種要求使用前重新驗證的策略。
  • 有內容指紋的靜態資產可以快取一年:例如 app.8f31c.css。CSS 或 JavaScript 更新後,檔名也會改變,瀏覽器會把它視為全新的檔案,因此適合搭配 immutable 使用一年 TTL。
  • 固定檔名的資產應使用短 TTL:例如 style.cssapp.js。如果內容更新但 URL 不變,長效快取可能讓訪客持續取得舊檔案;在尚未完成版本化時,可先使用 5–30 分鐘。
  • Pagefind 使用 no-cache:搜尋索引會隨文章更新,但 URL 通常不一定包含內容指紋,因此每次使用前都應重新驗證。
  • 正在除錯時全部縮短 TTL:先讓 HTML、CSS 和 JavaScript 使用 no-cache 或短 TTL,確認部署與 response headers 正常後,再恢復長效快取。

要注意的是,這張表是判斷原則,不代表目前 .htaccess 已經自動檢查檔名是否含有內容指紋。目前的 <FilesMatch> 是按照副檔名設定一年快取,所以固定名稱的 style.cssapp.js 也可能被快取一年。只有在部署流程確實會讓資產 URL 隨內容改變時,才適合使用一年 TTL 和 immutable

常見問題

重新建置後,網站為什麼還是舊頁面?

先確認 _site 裡真的有新內容,再確認上傳的是 _site 內容,而不是原始 Markdown。接著依序檢查 Hostinger 快取、CDN 和瀏覽器,不要只重跑 11ty。

可以把所有檔案都設定成快取一年嗎?

不建議。沒有版本化的 HTML、style.cssapp.js 可能長時間讓訪客拿到舊內容。只有 URL 會隨內容改變的資產,才適合搭配 immutable 使用一年 TTL。

為什麼 Pagefind 搜尋不到剛發布的文章?

可能是 Pagefind 索引本身沒有重新建置,也可能是 /pagefind/ 資產仍被舊快取提供。先確認部署輸出有最新索引,再用 curl -I 檢查 Pagefind URL 是否真的要求重新驗證,最後清除 Hostinger 或 CDN 快取。

.htaccess 後網站出現 500 怎麼辦?

先還原最後一次修改,確認 Hostinger 支援 mod_headersmod_expires 和相關語法,再逐段加入設定。不要在未驗證前,直接把同一份 .htaccess 複製到其他網站。

部署驗收工具

  • Hostinger hPanel:用來清除網站快取、管理 Cache Manager,以及依方案管理 CDN。
  • curl:直接查看公開網站的 response headers,先確認伺服器實際回傳什麼,再判斷是不是瀏覽器問題。
  • Chrome DevTools Network:查看 AgeETagCache-Control 和 response body,交叉確認不同資產是否使用預期的 TTL。

結語:先分層,再決定 TTL

11ty 快取問題的重點不是把數字設得越大越好,而是讓不同檔案採用適合自己的更新策略:

  1. HTML 使用短 TTL,保持容易更新。
  2. 版本化 CSS、JavaScript、圖片和字型使用長效快取。
  3. Pagefind 搜尋資產使用 no-cache,避免索引更新被卡住。
  4. 每次部署後用 hPanel、curl 和無痕視窗驗收。

完成設定後,下一步是到 Hostinger hPanel 清除快取,再用 curl -I 確認公開網站真的回傳預期的 headers。快取驗收完,再來一杯咖啡;這次咖啡和 HTML 都應該是熱的。

參考資料