0%

正威網路行銷工作室,專注於基隆、台北與全台客製化網頁設計,提供專業 RWD 響應式、SEO 搜尋引擎優化、品牌形象設計與網站維護託管服務。

搜尋文章
聯絡資訊
地址基隆市仁二路 220 號 5 樓
追蹤我們

瀏覽器快取設定總整理:.htaccess 的 Expires、Cache-Control 寫法與檔案更新不生效的解法

未來科技概念的立體插圖

訪客第一次打開你的網站時,瀏覽器會下載網頁用到的所有檔案:圖片、CSS、JavaScript、字型。瀏覽器快取就是讓瀏覽器把這些檔案存起來,下次再來、或點到第二頁時直接拿出來用,不用再跟主機要一次。

如果主機沒有告訴瀏覽器「這個檔案可以用多久」,瀏覽器常常得先問主機「檔案有沒有變」,就算答案是沒變,一來一回也要花時間。設定了有效期限之後,期限內瀏覽器連問都不用問,頁面自然就快了。

這篇把設定方法、推薦的有效期限,以及最常遇到的「改了 CSS 訪客卻看到舊版」問題整理在一起。(原本另一篇〈通過設置.htaccess給WordPress站點Leverage browser caching〉的內容已經整合到這裡。)

瀏覽器快取和快取外掛有什麼不同?

  • 快取外掛(伺服器端快取):WP Rocket、LiteSpeed Cache 這類外掛,是把整頁網頁先產生好存在主機上,省掉每次執行 PHP 與查詢資料庫的時間,對每一位訪客都有幫助。
  • 瀏覽器快取:檔案存在訪客的電腦或手機上,對回訪的訪客、以及同一次造訪中點開的第二、第三頁有幫助。Logo、CSS、JavaScript 每一頁都一樣,只要下載一次就好。

兩者是互相搭配,不是二選一。

Expires 與 Cache-Control 是什麼?

主機是用回應標頭(HTTP header)告訴瀏覽器檔案可以快取多久,常見的有兩個:

  • Expires:寫一個固定的到期時間,在這之前都有效。
  • Cache-Control: max-age=秒數:寫「從現在起幾秒內有效」,例如 max-age=31536000 是一年。這是比較新的寫法,兩個同時存在時,瀏覽器以 Cache-Control 為準。

Cache-Control 還有幾個常見的值:

  • no-cache:可以存,但每次使用前都要先問主機有沒有更新。
  • no-store:完全不要存,通常用在含有個人資料的頁面。
  • public、private:能不能讓 CDN 等中間的快取伺服器也存一份。

Apache 的 mod_expires 模組會同時送出 Expires 與 Cache-Control: max-age,一般網站用它設定就夠了。

設定之前:先看網站現在的狀況

有些主機、快取外掛或 CDN 已經幫你設定好了,先檢查可以避免重複設定:

  1. 用 Chrome 打開網站,按 F12 切到「Network(網路)」分頁,確認上方的「Disable cache(停用快取)」沒有勾選。
  2. 重新整理頁面,點一個 .css 檔或圖片。
  3. 在「Response Headers(回應標頭)」裡,看有沒有 cache-control 的 max-age,或是 expires。
  4. 再重新整理一次,如果「Size(大小)」欄位顯示 memory cache 或 disk cache,代表這個檔案是直接從瀏覽器快取拿出來的。

Apache:用 mod_expires 設定(推薦寫法)

Apache 與 LiteSpeed 主機,在網站根目錄的 .htaccess 最上面(# BEGIN WordPress 那一行的上面)加入下面這段。改之前記得先備份原本的檔案:

Apache (.htaccess)
<IfModule mod_expires.c>
  ExpiresActive On

  # 網頁與資料:每次都向主機確認有沒有新版本
  ExpiresByType text/html "access plus 0 seconds"
  ExpiresByType application/json "access plus 0 seconds"
  ExpiresByType application/xml "access plus 0 seconds"
  ExpiresByType text/xml "access plus 0 seconds"

  # RSS 訂閱
  ExpiresByType application/rss+xml "access plus 1 hour"
  ExpiresByType application/atom+xml "access plus 1 hour"

  # CSS 與 JavaScript (網址有版本號,可以放很久)
  ExpiresByType text/css "access plus 1 year"
  ExpiresByType application/javascript "access plus 1 year"
  ExpiresByType text/javascript "access plus 1 year"

  # 字型
  ExpiresByType font/woff2 "access plus 1 year"
  ExpiresByType font/woff "access plus 1 year"
  ExpiresByType font/ttf "access plus 1 year"
  ExpiresByType font/otf "access plus 1 year"

  # 圖片
  ExpiresByType image/jpeg "access plus 1 month"
  ExpiresByType image/png "access plus 1 month"
  ExpiresByType image/gif "access plus 1 month"
  ExpiresByType image/webp "access plus 1 month"
  ExpiresByType image/avif "access plus 1 month"
  ExpiresByType image/svg+xml "access plus 1 month"
  ExpiresByType image/x-icon "access plus 1 week"
  ExpiresByType image/vnd.microsoft.icon "access plus 1 week"

  # 影音
  ExpiresByType video/mp4 "access plus 1 month"
  ExpiresByType video/webm "access plus 1 month"
  ExpiresByType audio/mpeg "access plus 1 month"
</IfModule>

這樣設定的理由:

  • HTML、JSON、XML 設 0 秒:網頁內容隨時會更新,每次都要向主機確認,訪客才會看到最新的文章。WordPress 登入後的後台頁面會自己送出「不要快取」的標頭,這段設定不會去改動它們。
  • CSS、JavaScript、字型設一年:WordPress 載入這些檔案時,網址後面會加上 ?ver=版本號,主題或外掛更新時網址就會改變,瀏覽器會當成新檔案重新下載,所以可以放心設很久(自己改檔案時要注意,下面會說明)。
  • 圖片設一個月:WordPress 上傳的圖片很少會用同一個檔名覆蓋,如果你確定不會這樣做,也可以延長到一年。
  • RSS 設一小時、網站小圖示(favicon)設一週:兼顧更新速度與效能。
  • 沒列出的檔案類型(例如 PDF)維持主機原本的行為。PDF 型錄、價目表常常用同一個檔名更新,不適合設長期快取。

外面的 IfModule 代表主機有 mod_expires 模組才會執行,萬一沒有也不會讓網站出錯。

檔案改了,訪客看到的還是舊的?用版本號解決

快取設得越久,越要注意這個問題:瀏覽器在期限內不會再問主機,你改了 CSS,訪客的瀏覽器還是拿舊檔案來用。

解決的原理很簡單:檔案內容變了,就讓網址也跟著變。瀏覽器看到新網址,會當成新檔案重新下載。

WordPress:用 ver 參數

WordPress 用 wp_enqueue_style()、wp_enqueue_script() 載入檔案時,第四個參數就是版本號,會變成網址後面的 ?ver=...。自己在子主題加的 CSS,可以用檔案的修改時間當版本號,每次存檔就會自動換成新網址。把下面這段加到子主題的 functions.php:

PHP
add_action( 'wp_enqueue_scripts', function () {
    $file = get_stylesheet_directory() . '/css/custom.css';
    if ( ! file_exists( $file ) ) {
        return;
    }
    wp_enqueue_style(
        'my-custom-style',
        get_stylesheet_directory_uri() . '/css/custom.css',
        array(),
        (string) filemtime( $file )
    );
} );

css/custom.css 換成你實際的檔案位置。JavaScript 改用 wp_enqueue_script(),寫法相同。

其他常見情況

  • 版型裡直接寫死 <link> 的網站(非 WordPress、或自己寫的版型):在網址後面手動加上版本號,例如 style.css?v=2,每次更新檔案就把數字加一。
  • 用同一個檔名覆蓋圖片:訪客可能在快取期限內都看到舊圖。建議上傳新圖片(新檔名),再把文章裡的圖片換掉。
  • 後台「外觀 → 自訂 → 附加的 CSS」:這裡的 CSS 是直接寫在網頁裡,不受瀏覽器快取影響;但如果有快取外掛,存檔後要記得清除外掛的快取。
  • 自己檢查修改結果:用 Ctrl + Shift + R(Mac 是 Cmd + Shift + R)強制重新整理,瀏覽器會重新下載所有檔案。

舊版寫法為什麼要換掉

這篇文章舊版與合併進來的另一篇,程式碼都有需要修正的地方:

  • 搬家時格式跑掉:原本的 IfModule、FilesMatch 標籤被吃掉了,照著貼上會讓網站出現 500 錯誤。
  • 過時的檔案類型:text/x-component(只有舊版 IE 使用)、text/cache-manifest(瀏覽器已經移除的離線快取功能),以及 application/font-woff2 這類舊的字型類型名稱,現在都用不到或已經改名。
  • 期限設反了:CSS、JavaScript 有版本號,可以放很久,舊寫法卻只設兩小時;PDF 這類常用同一個檔名更新的文件,反而設了將近一年,更新後訪客可能一直下載到舊版。
  • 把所有 PHP 檔案設成 no-store:WordPress 的每一頁都是透過 index.php 產生,這等於整個網站的網頁都禁止快取,瀏覽器按「上一頁」時也可能無法直接使用暫存的頁面。WordPress 真正需要禁止快取的頁面(例如登入後的後台)本來就會自己處理。
  • Pragma: no-cache:這是 HTTP/1.0 時代的寫法,現在用 Cache-Control 就好。

Nginx 主機

Nginx 不讀 .htaccess,對應的寫法是:

Nginx
location ~* \.(?:css|js|woff2?|ttf|otf)$ {
    expires 1y;
}

location ~* \.(?:jpe?g|png|gif|webp|avif|svg|ico|mp4|webm)$ {
    expires 1M;
}

expires 會同時送出 Expires 與 Cache-Control: max-age,1y 是一年、1M 是一個月。Nginx 設定通常要請主機商處理;如果主機已經有處理靜態檔案的 location 設定,自己再加可能會互相衝突,出現錯誤時請主機商協助。

用 PageSpeed Insights 檢查

  1. 打開 PageSpeed Insights,輸入網址進行分析。
  2. 在診斷結果裡,找跟快取有關的項目(英文介面叫 Use efficient cache lifetimes,較早的版本叫 Serve static assets with an efficient cache policy)。
  3. 展開後會列出快取時間太短的檔案,以及它們目前的快取期限。

清單裡常會出現 Google Analytics、廣告、YouTube、Facebook 這類外部網站的檔案,它們的快取時間由對方決定,你沒辦法修改;只要確認自己網站的檔案沒有被列出就好。

常見問題

用了快取外掛或 Cloudflare,還需要自己設定嗎?

先用前面的方法檢查。有些快取外掛(例如 LiteSpeed Cache)有瀏覽器快取的選項,開啟後就會送出這些標頭,已經有了就不要重複設定。Cloudflare 的「瀏覽器快取 TTL」設定也可能改變最後送出的期限,兩邊的設定要一致。

網站改版之後,有訪客反映版面怪怪的?

多半是舊的 CSS 還在他的瀏覽器裡。確認新的 CSS 網址有換版本號,並清除快取外掛與 CDN 的快取;請訪客強制重新整理一次,也能馬上解決。

快取期限設多久才好?

上面推薦的期限適合大多數網站。原則是:網址會跟著內容改變的檔案(有版本號的 CSS、JavaScript),期限越長越好;網址不變、內容卻可能更新的檔案(網頁本身、PDF),不要設長期快取。

延伸閱讀

網站速度是很多小設定累積出來的,正威網路行銷是 25 年經驗的基隆網頁設計工作室,提供客製化網頁設計與網站維護託管,也服務台北與全台客戶,歡迎來聊聊。

分享這篇文章

返回文章列表更多「Wordpress代碼教學」文章

發表留言

還沒有人留言,歡迎第一個分享你的想法或問題。留言經審核後才會顯示,Email 不會公開。

讓我們一起打造
您的專屬網站!