NGINX cache key中如何移除網址中不必要的參數

對一個大流量的網站,cache可說是無所不在。由訪客端的browser cache、傳輸過程中的proxy cache,到程式端的nosql等各種cache…對於cache機制,最重要的指標莫過於hit rate。這數字越高,表示該cache機制的效果越佳。

最近在處理一個爬蟲的過程中,注意到一個狀況,當行銷部門買Google廣告、或有網友將網址分享於FB時,Google或Facebook都會在網址後面加上他們自己獨有GET參數。

由於我們有採用nginx作為reverse proxy,主要用以降低後端的web server的loading(還有其他用途),上述的狀況會使得hit rate不好。

為何會造成hit rate不好?以下面三個網址為例,他們對訪客而言內容相同,其實都會認知為同一個網址,但在reverse proxy中就會被視為三個不同的網址、產生三個cache key。因此降低了hit rate、也佔了不必要的空間
https://test.com.tw/
https://test.com.tw/?gclid=RAbEiwAE_ZzPcgLJLeUQJzqPKdPX3mumt9RqVox4nKRggR
https://test.com.tw/?gclid=kgpftEMocqU2VgJoaRGOj0kPnqZ6sIcPHAUuhoCl4AQAvD

要如何讓nginx將上述三個網址產生同一個cache key呢?我的作法如下…
#取得nginx變數-$args
set $c_args $args;
#判斷參數中是否有gclid,如果有,則移除
if ($c_args ~ (.*)(?:&|^)gclid=[^&]*(.*)) {
  set $c_args $1$2;
}
#cache key的組成如下
#如果只有一個host,$host可以省略不放
proxy_cache_key $host$uri$is_args$c_args;

===2020/01 補充 開始===
如需同時移除Facebook 的fbclid,上述條件判斷的正規表達稍做調整即可,如下…
#取得nginx變數-$args
#判斷參數中是否有gclid或fbclid,如果有,則移除
if ($c_args ~ (.*)(?:&|^)(?:fbclid|gclid)=[^&]*(.*)) {
  set $c_args $1$2;
}
===2020/01 補充 結束===

原以為一切正常。但在測試purge功能時發現上面的寫法有個問題,有時會無法purge。最後發現原因在於nginx變數-$is_args

官方文件是這樣描述$is_args
“?” if a request line has arguments, or an empty string otherwise

因nginx變數-$is_args會因為網址中是否有參數,傳回不同結果,所以經過上面處理過後的cache key,其實會產生兩組,說明如下…
#以下網址cache key的組成為 test.com.tw/
https://test.com.tw/
#以下網址cache key的組成為 test.com.tw/?
https://test.com.tw/?gclid=RAbEiwAE_ZzPcgLJLeUQJzqPKdPX3mumt9RqVox4nKRggR
#以下網址cache key的組成為 test.com.tw/?
https://test.com.tw/?gclid=kgpftEMocqU2VgJoaRGOj0kPnqZ6sIcPHAUuhoCl4AQAvD

最後,我統一保留『?』,讓cache key的組成統一為『test.com.tw/?』
調整如下~
#取得nginx變數-$args
set $c_args $args;
#判斷參數中是否有gclid
if ($c_args ~ (.*)(?:&|^)gclid=[^&]*(.*)) {
  set $c_args $1$2;
}
#cache key的組成中,直接放入『?』
#統一為 test.com.tw/?
proxy_cache_key $host$uri?$c_args;


參考資料

留言