[WordPress] FastCGI 快取網站加速,Nginx Helper 特別加強版本

本篇文章更新時間:2023/06/04
如有資訊過時或語誤之處,歡迎使用 Contact 功能通知。
一介資男的 LINE 社群開站囉!歡迎入群聊聊~
如果本站內容對你有幫助,歡迎使用 BFX Pay 加密貨幣新台幣 贊助支持。


Nginx 裡用來處理 PHP-CGI 運作需求的模組 ngx_http_fastcgi_module,其中有一個說他雞肋但又真能加速的快取功能叫「FastCGI Cache」。

雞肋的點是因為他就只會「照你的方式」快取,但要刪除它...就請自理,整個很工程(難)。

所以要處理它會分兩個部分「設定快取邏輯」與「設定對應刪除邏輯」。

Google Nginx FastCGI Cache 快取 WordPress 等關鍵字可以找到一堆差不多的教學,這邊標題寫特別版就是真的跟大部分介紹文章不同。

網路上的文章千篇一律都是教同一個設定快取的方式,然後 WordPress 端搭配 Nginx Helper 外掛來做清除。

但那樣的使用情境有一個前提:你的網站是 RWD 響應式設計且不會因為裝置不同而有後端運算處理資料的不同。

這樣一來只需要把網頁快取著一份,清理的時候也只需要清理一份就好了!

但是,如果我的 WordPress 網站使用了雙主題架構或是特別處理,導致在手機或平板瀏覽下是 A 主題,電腦版則是 B 主題時,只快取其中一份都不對吧?

簡單來說,同一個網址如果因為裝置請求不同而有不同呈現的話,原本預設快取方式就有問題。

設定快取邏輯

這邊要先談一下 FastCGI 快取運作的機制,這功能宣告使用一個空間與限制大小後,透過設定一組 key 來做 md5 加密且把快取資料存在那空間裡。

fastcgi_cache_key "$scheme$request_method$host$request_uri";

這組 key 透過幾個參數組合來達到獨特性,結構就跟網址差不多,結果如下:httpsGETwww.mxp.tw/blog/

FastCGI 模組將上述範例 key 加密後得到 6feae0f210b851e9fb54f21440aaf6c0 這個快取檔案名,並把快取資料(網站頁面原始碼)存放於指定特殊路徑下。

特殊路徑的規則也很簡單,取上述 md5 加密檔案名的後三碼為路徑資料夾,最後 1 碼為第一層,最後第 2 與第 3 為第二層。所以假設我指定存放快取的根路徑是 /tmp/nginx-cache/ 的話,那這份快取就存在 /tmp/nginx-cache/0/6c/6feae0f210b851e9fb54f21440aaf6c0 這裏。

看到這就會發現,如果還要區分手機版或電腦版的話,還要讓 Nginx 幫忙判斷一下 User Agent 請求來源。

map $http_user_agent $is_desktop {
    default 0;
    ~*linux.*android|windows\s+(?:ce|phone) 0; # exceptions to the rule
    ~*spider|crawl|slurp|bot 1; # bots
    ~*windows|linux|os\s+x\s*[\d\._]+|solaris|bsd 1; # OSes
}

## Revert the logic.
map $is_desktop $is_mobile {
    1 0;
    0 1;
}
add_header x-ua-device $is_mobile;

Gist 來源

所以原本的 key 就要更新成:

fastcgi_cache_key "$scheme$request_method$host$request_uri$is_mobile";

讓請求來源不同,就建立不同的快取版本!

所以主要差異就如上所說,下方把設定統整一下:

http{

[略]

map $http_user_agent $is_desktop {
    default 0;
    ~*linux.*android|windows\s+(?:ce|phone) 0; # exceptions to the rule
    ~*spider|crawl|slurp|bot 1; # bots
    ~*windows|linux|os\s+x\s*[\d\._]+|solaris|bsd 1; # OSes
}

## Revert the logic.
map $is_desktop $is_mobile {
    1 0;
    0 1;
}
add_header x-ua-device $is_mobile;
fastcgi_cache_path /tmp/nginx-cache levels=1:2 keys_zone=WORDPRESS:256m inactive=1d;

[略]

server {
[略]

#設定是否略過快取的開關,0是不略過
set $skip_cache 0;
#POST請求一率略過
if ($request_method = POST) {
    set $skip_cache 1;
}
#有傳GET參數的也略過,避免問題一堆與不好清除快取
if ($query_string != "") {
    set $skip_cache 1;
}

#指定的頁面略過
if ($request_uri ~* "/wp-admin/|/go/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
    set $skip_cache 1;
}

#判斷 cookie 登入用戶也略過快取
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
    set $skip_cache 1;
}

#正在維護模式中也略過
if (-f "$document_root/.maintenance") {
    set $skip_cache 1;
}

[略]

location ~ [^/]\.php(/|$) {
    fastcgi_pass  unix:/tmp/php-cgi.sock;
    fastcgi_index index.php;
    include fastcgi.conf;
    include pathinfo.conf;
    fastcgi_cache_key "$scheme$request_method$host$request_uri$is_mobile";
    fastcgi_cache_use_stale error timeout invalid_header http_500;
    fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;
    add_header X-Cache "$upstream_cache_status From $host";
    fastcgi_cache WORDPRESS;
    fastcgi_cache_valid  200 301 302 1d;
}

[略]
}
}

設定好記得 nginx -t 驗證,沒問題後就重新載入設定,透過 Chrome 瀏覽器開發者工具驗證不同裝置請求頁面,且伺服器回應的 Header 有沒有看到 x-cache: HIT From 你的網域

這邊的需求網路上也有看到一篇討論:How to - Fastcgi_cache & Desktop with Mobile versions - Purging with GET Requests 可以參考。

如果上述沒問題的話就要進行到清除的部分。

設定對應刪除邏輯

其實刪除的邏輯上面已經提到,只是改由 WordPress 這邊再實作一次。流程就是把網站上要清除快取的連結,使用 FastCGI 設定 key 的組成方法,重組回那個 md5 加密的檔名,然後使用 PHP 的方法移除該快取檔案就刪除完成了!

網路上大家推的 Nginx Helper 外掛很可惜沒有辦法透過設定的 hook 來去實現本篇說的情境。架構上還要調整,以及也還有點問題要修。

所以我就把這款外掛中 FastCGI 快取清除的部分抽離與修改,發布了一套純用在清除 Nginx FastCGI 快取的外掛:Nginx FastCGI Cache 清除小幫手

剛寫出來,還沒整理,就是一個能用的版本。設定存放快取路徑的 RT_WP_NGINX_HELPER_CACHE_PATH 常數沿用 Nginx Helper,預設值則是改成我用的 /tmp/nginx-cache

如使用上碰到 BUG,歡迎開 issue ~

後記

其實網路上還會補上一個 Nginx 模組「ngx_cache_purge」,使用 API 請求方式來刪除快取,儘管 FRiCKLE 的版本表示已達可商用等級,不過超過五年沒更新,放著 issue 沒想解,我就挑了 tordenv2.3.1 版分支(2020/01 釋出)來試試,除了 purge_all 功能一直試不出來,其他都正常在 Nginx/1.16.1 版本下運作。

重新編譯 Nginx 時補上 --add-module=/path/to/src/ngx_cache_purge-2.3.1 即可

但!! 如果能在執行 PHP 的時候順便刪除檔案,為何還要繞一個 URL API 「再」請 Nginx 來刪呢? 所以這也是我外掛不打算實作這種清除快取方式的原因。

其他像是把這類似機制套上 Redis 的 SR Cache 就改天有時間再來測試了,畢竟簡單有效就能處理的才討喜呀XD


Share:

作者: Chun

資訊愛好人士。主張「人人都該為了偷懶而進步」。期許自己成為斜槓到變進度條 100% 的年輕人。[///////////____34%_________]

文章
Filter
Apply Filters