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

這篇會用一套實際設定,帶你分開處理 HTML、CSS、JavaScript、圖片、字型和 Pagefind 搜尋索引,最後用 curl 驗收更新是否真的生效。

先記住這個快取原則

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

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

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

11ty 與 Hostinger 各自負責什麼?

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

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

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

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

部署前先確認 _site

在專案根目錄執行:

npx @11ty/eleventy

接著確認 _site 裡有:

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

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

public/.htaccess 設定快取

目前網站使用 public/.htaccess 管理 Apache 的過期時間與 response headers。實際設定分成 mod_expiresmod_headersmod_setenvif 幾個區塊。注意:不是所有圖片和字型都會拿到一年快取,真正的範圍由 <FilesMatch> 的副檔名規則決定。

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresDefault "access plus 2 days"
  ExpiresByType text/html "access plus 1 day"
  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 "\.(?: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 "public, max-age=86400" env=pagefind_asset
</IfModule>

這些設定分別做什麼?

  • ExpiresActive On:啟用 Apache 的過期時間設定。
  • ExpiresDefault "access plus 2 days":沒有特別指定類型的檔案,預設快取兩天。
  • ExpiresByType text/html "access plus 1 day":HTML 快取一天,讓文章與首頁不會長時間卡在舊版本。
  • CSS、JavaScript、圖片和字型的 ExpiresByType:先設定為快取一個月。
  • Cache-Control: public, max-age=31536000, immutable:把符合檔名規則的 CSS、JavaScript、部分圖片、圖示與 woff 字型實際設定為快取一年。
  • SetEnvIfNoCase Request_URI "^/pagefind/" pagefind_asset:找出 /pagefind/ 開頭的檔案。
  • Header set Cache-Control "public, max-age=86400" 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: public, max-age=86400

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

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

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

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

  • 靜態網站路由:使用 mod_rewrite 將找不到實體檔案或目錄的請求導向入口頁,例如 /index.html。如果網站使用 11ty 產生檔案式路由,才需要採用符合專案結構的 rewrite 規則。
  • 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
  2. 如果只有單一頁面或 URL 過期,到 Advanced → Cache Manager 清除指定 URL。
  3. 如果網站啟用了 Hostinger CDN,進入 CDN 設定執行 Flush/Purge
  4. 用無痕視窗重新開啟首頁和剛更新的文章。

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

curl 驗收 response headers

檢查首頁:

curl -I https://example.com/

檢查一個版本化資產:

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

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

首頁應該看到短 TTL 或要求重新驗證的設定;版本化 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 一天;其他資產依版本策略避免搜尋結果長時間不完整
CDN 剛啟用或剛清除短 TTL依 CDN 規則等待節點更新,再用不同網路測試

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

  • HTML 通常使用短 TTL:首頁和文章內容可能經常更新,因此使用 no-cache 或短時間快取,讓瀏覽器重新向伺服器確認。no-cache 不是完全不儲存檔案,而是使用快取前必須重新驗證。
  • 有內容指紋的靜態資產可以快取一年:例如 app.8f31c.css。CSS 或 JavaScript 更新後,檔名也會改變,瀏覽器會把它視為全新的檔案,因此適合搭配 immutable 使用一年 TTL。
  • 固定檔名的資產應使用短 TTL:例如 style.cssapp.js。如果內容更新但 URL 不變,長效快取可能讓訪客持續取得舊檔案;在尚未完成版本化時,可先使用 5–30 分鐘。
  • Pagefind 使用一天 TTL:搜尋索引會隨文章更新,但 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 是否真的使用一天 TTL,最後清除 Hostinger 或 CDN 快取。

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

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

推薦工具/資源

  • Hostinger hPanel:適合已經把 11ty 靜態站部署到 Hostinger,需要管理 Cache Manager、CDN 或網站根目錄的站長。若你正在選擇支援靜態網站部署與 hPanel 的主機,可參考 Hostinger 主機服務 →
  • curl:免費且適合直接查看公開網站的 response headers,先用它確認伺服器實際回傳什麼,再判斷是不是瀏覽器問題。
  • Chrome DevTools Network:適合查看 AgeETagCache-Control 和 response body,確認不同資產是否使用了預期的 TTL。

結語:先分層,再決定 TTL

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

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

完成設定後,下一步是到 Hostinger hPanel 清除快取,再用 curl -I 確認公開網站真的回傳預期的 headers:先到 Hostinger 管理你的靜態網站與快取 →