專案

一般

設定檔

動作

瀏覽器快取

一般背景:為何瀏覽器快取是件好事

在任何網站上,讓瀏覽器快取靜態物件(例如不包含使用者內容,且在數小時、數天或數週內都相同)是一種良好的做法。

(這通常指的是像 .css、.js 和圖片檔這樣的檔案)

但有些內容會快速變更,因此不能被瀏覽器快取。

如果瀏覽器被告知「不要快取這個物件」
那麼它就知道不要快取。這是在可能變更的頁面上,網站(和 Redmine)會做的事情:例如,在議題頁面上,它會將訊息放入 HTTP 標頭中。

但是,如果沒有這個訊息,也就是說,如果瀏覽器不確定它最近下載的頁面元件
它會向伺服器發送 304 請求:詢問「你能告訴我,這個檔案是否還沒有過期嗎?」。對於 304 請求,伺服器不需要再次發送整個物件:只需要一個簡短的「是的,它仍然是新的」回應。

在 Redmine 的預設設定中,它會在每個頁面上產生大量的 304 連線:您可以在像 Firebug 這樣的工具中看到這些連線。每個 .css 和 .js 檔案等等都會有一個連線。非常多。

這些對使用者體驗不利:因為瀏覽器必須等待這些回應,然後才能繼續建構頁面。

所以,我們需要告訴瀏覽器,這些物件在很長一段時間內都不會過期

有一些簡單的方法可以設定 Apache 和 nginx 來做到這一點:告訴它們在 HTTP 標頭中設定一個遙遠的「過期」日期:這樣瀏覽器就知道:「好的,這個物件沒有過期,因為我們還沒有超過過期日期」。

因此,當新使用者訪問 Redmine 時,他們的瀏覽器在第一個頁面上會 GET .css 和 .js 檔案等,但在之後的頁面上就不需要再次取得它們了。

使用者將會體驗到更快的網頁建構速度!

Redmine 的問題

不幸的是,Redmine(相當不明智地)將 .js 檔名用於確實包含使用者內容的內容:也就是說,不應該在瀏覽器中快取的內容。

所以,這意味著:如果您在 Apache/nginx 中添加了一個簡單的設定,用於快取任何名稱為 *.js 的內容:那麼您的 Redmine 就會出問題!

請參閱議題 #17770 - 這個問題在這裡被報告,看看 Redmine 團隊是否可以改變它,停止在不適當的地方使用 .js。

如果您使用簡單的配置,它會在以下情況中斷:(a) 在問題中編輯日誌時 (b) 將檔案上傳到問題時

Redmine 的解決方法

簡單的情況,會像上面那樣破壞 Redmine,因為它不關心 js 檔案所在的目錄:例如

location ~* \.(ico|css|js|gif|jp?g|png)(\?[0-9]+)?$ {
expires 365d;
}

所以需要使用更複雜的配置:在設置緩存標頭之前,還要檢查 .js 檔案所在的目錄。

我讓它運作起來了

        # the regex logic:   after either /javascripts/ or /stylesheets/ find the suffixes we want, followed by any quantity of numbers 0-9
        #     This works because the files we want to cache always appear after one of those 2 directories:  but not the files we want to ignore
        #        /journals/edit/24174.js   and /uploads.js?attachment_id=1&filename=my-file-to-upload.png
        location ~* /(?:(?:plugin_assets|themes).+)?(javascripts|stylesheets|images|favicon).+(css|js|jpg|gif|ico|png)(\?[0-9]+)$ {
            # add_header  X-SV-test 304-killer;    use this do-nothing HTTP Header, if you need to play with the regexp 
            #- for testing without fear of breaking anything!
            expires 365d;
        }

從 Redmine 3.4.0 (#24617) 開始,靜態檔案請求會附加一個 ?timestamp,以避免升級 Redmine 版本和緩存提供錯誤檔案時出現問題。 但是,自定義主題不會在請求中附加時間戳,因此 application.css 和 responsive.css 可能會嚴重損壞。 有關解決方法,請參閱 #29625

如果您想確保安全,請勿緩存沒有 ?timestamp 的檔案

-  # the regex logic: after either /javascripts/ or /stylesheets/ find the suffixes we want, followed by any quantity of numbers 0-9
+  # the regex logic: after either /javascripts/ or /stylesheets/ find the suffixes we want, followed by one or more numbers 0-9
   # This works because the files we want to cache always appear after one of those 2 directories:  but not the files we want to ignore
   # /journals/edit/24174.js  and /uploads.js?attachment_id=1&filename=my-file-to-upload.png
-  location ~* /(?<file>/(?:(?:plugin_assets|themes).+)?(?:javascripts|stylesheets|images|favicon).+(?:css|js|jpe?g|gif|ico|png|html|woff|ttf|svg)(\?[0-9]+)?$) {
+  location ~* /(?<file>/(?:(?:plugin_assets|themes).+)?(?:javascripts|stylesheets|images|favicon).+(?:css|js|jpe?g|gif|ico|png|html|woff|ttf|svg)(\?[0-9]+)$) {

請注意,在 nginx 中:'expires' 是在物件中設置 'Expiry' HTTP 標頭的配置。

在上面,365d 表示 365 天。

有關完整的 nginx 'expires' 配置詳細信息,請參閱:https://nginx.dev.org.tw/en/docs/http/ngx_http_headers_module.html#expires
對於 Apache:請參閱 mod_expires:https://apache-httpd.dev.org.tw/docs/2.2/mod/mod_expires.html

salman mp 更新於 12 個月前 · 19 個修訂