【記錄】那些很實用的Nginx規則

天府雲創發表於2018-01-16

1. 概述

大家都知道Nginx有很多功能模組,比如反向代理、快取等,這篇文章總結下我們這些年實際環境中那些有用的Nginx規則和模組,大部分是用法的概括及介紹,具體細節在實際配置時再自行google。

2. 內建語法

先介紹Nginx預設已支援的內建功能,靠這些基本就滿足大部分的web服務需求。

2.1 proxy代理

proxy常用於兩類應用場景,一類是中轉,如異地科學的上網方式,另外一類是到後端服務的負載均衡方案。

用反向代理時候,需要特別注意裡面的域名預設是在nginx啟動時候就解析了,除非reload否則一直用的是當初解析的域名,也就是說不能動態解析。

但這個問題是可以通過別的模組或者用內建字典變數方式來解決。

resolver 114.114.114.114;
server {
    location / {
        set $servers github.com;
        proxy_pass http://$servers;
    }
}

2.1.1 中轉

針對某個域名進行中轉:

server {
listen 172.16.10.1:80;
    server_name pypi.python.org;
    location ~ /simple {
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://pypi.python.org;
    }
}

注意如果是前後端域名不一樣的話需要處理proxy_redirect的301跳轉之類的顯示,否則在跳轉時候會跳轉到proxy_pass的域名。

另外可以直接代理所有80埠的http流量:

server {
    listen 80;
    server_name _;
    resolver 114.114.114.114;
    set $URL $host;
    location / {
        proxy_pass http://$URL;
    }
}

如果是想代理https的站點也不是不可能,只是需要自行處理CA證照匯入即可,而且經過https中轉的流量對nginx是透明的,也就是有證照的時候做竊聽和劫持的情況。

2.1.2 負載均衡

這是代理的另外一個常見用法,通過upstream到多個後端,可以通過weight來調節權重或者backup關鍵詞來指定備份用的後端,通常預設就可以 了,或者可以指定類似ip_hash這樣的方式來均衡,配置很簡單,先在http區域新增upstream定義:

upstream backend {
    ip_hash;
    server backend1.example.com weight=5;
    server backend2.example.com weight=5;;
}

然後在server裡面新增proxy_pass:

location / {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

做負載均衡的時候可以智慧識別後端伺服器狀態,雖然可以智慧地proxy_next_upstream到另外的後端,但還是會定期損失一些正常的“嘗試性”的連線,比如過了max_fails 次嘗試之後,休息fail_timeout時間,過了這個時間之後又會去嘗試,這個時候可以使用第三方的upstream_check模組來在後臺定期地自動探索,類似這樣:

check interval=3000 rise=2 fall=5 timeout=2000 type=http;

這樣替代使用者正常的連線來進行嘗試的方式進一步保障了高可用的特性。

還有就是在做前端代理的時候也是這樣的方式,直接proxy_pass到後端即可,比如CDN的場景。

2.2 防盜鏈

普通的防盜鏈是通過referer來做,比如:

location ~* \.(gif|jpg|png|bmp)$ {
    valid_referers none blocked *.example.com server_names ~\.google\. ~\.baidu\.;
    if ($invalid_referer) {
        return 403;
    }
}

再精細一點的就是URL加密,針對一些使用者IP之類的變數生成一個加密URL通常是針對檔案下載時候用到,可以通過openresty來寫lua指令碼或者是accesskey之類的模組來實現。

2.3 變數

nginx裡面支援正則匹配和變數配置,預設的變數比如remote_addr、request_filename、query_string、server_name之類的,這些組合在一起可以做很多規則,或者還有日誌裡面status、http_cookie等。

還有在進行多域名配置時候可以用萬用字元,比如:

server_name ~^(www\.)?(.+)$;
root /data/web/$2;

這樣就實現了自動進行域名的目錄指派。

變數方面,比如配置變數a=1:

set $a 1;

下面這個案例配合if判斷來做有更大的用處。

2.4 if判斷

nginx裡面支援一些簡單的if判斷,但是沒有多重邏輯的語法,多個判斷條件用起來需要結合變數的方式來實現,比如允許ip地址為10.10.61段和和192.168.100段的使用者訪問,其餘的拒絕,返回405狀態碼:

set $err 0;
    if ( $remote_addr ~ 10.10.61.){
        set $err 0;
    }
    if ( $remote_addr ~ 192.168.100.){
        set $err 0;
    }
    if ( $err = 1){
        return 405;
    }

這樣通過一個err變數比較巧妙實現了需求。

2.5 error_page

有用到後端proxy的地方需要加上這句話才可以傳到狀態碼到nginx:

fastcgi_intercept_errors on;

具體配置一般是配置到具體的錯誤URL頁面,比如:

#返回具體狀態碼
error_page 404 403 /4xx.html
#返回200狀態碼
error_page 404 403 =200  /error.html

或者採用callback的方式統一做處理:

error_page 404 403 = @fallback; 
location @fallback {
    proxy_pass http://backend;
    access_log /data/logs/404_error.log access;
}

這樣在重定向時不會改變URL,然後把404頁面直接返回。

2.6 rewrite

rewrite做一些301、302之類的跳轉,同時也可以在CDN前端做“去問號”快取的效果。

location /db.txt {
    rewrite (.*) $1? break;
    include proxy.conf;
}

另外最常見的跳轉寫法:

rewrite ^/game/(.*) /$1;

把/game/test跳轉為/test的效果,注意這樣是沒有狀態碼的,如果訪問正常就直接返回200狀態碼。

可以在後面加個permanent引數,就變為了301 Moved Permanently,或者新增redirect改為302跳轉。

同理,還可以進行多個正則匹配進行URL重組,比如:

rewrite ^/download/(.*)/lastest/(.*)$ /file/$1?ver=$2 break;

2.7 日誌欄位

想針對每個連線進行日誌留檔,可以在nginx日誌那裡配置好欄位,比如記錄cookie之類的資料。

在log_format欄位裡面加入$http_cookie變數即可。

另外post的資料可以永久保留在檔案裡面,比如用來做http的日誌備份,包括get和post的原始資料,把這個值開啟即可:

client_body_in_file_only  on;

然後post的資料就會儲存在nginx/client_body_temp資料夾裡面。

2.8 internal關鍵詞

這個關鍵詞很少見,但有時候是很有用的,比如在有很多規則時候,突然需要針對某個目錄轉為nginx內部處理。

location ^~ /upload/down/ {
alias /data/web/dts/dtsfile/down/;
internal;
}

2.9 try_files

字面意思是嘗試,後面可以接多個目錄或者檔案,比如kohana框架:

try_files $uri /index.php?$query_string;

先看是否有URL這個檔案,沒有的話再呼叫index.php來處理,或者支援狀態碼處理:

try_files /foo /bar/ =404;

沒有這兩個檔案的話返回404狀態。

2.10 auth認證

可以做簡單的使用者登入認證方式,其中的passwd_file得通過apache的htpasswd命令來生成。

auth_basic "Restricted";
auth_basic_user_file passwd_file;

認證通過之後每次訪問會在頭部新增Authorization欄位包含使用者名稱密碼的base64加密密文給服務端。

2.11 gzip

普通的線上web站點gzip壓縮是必須要開的,壓縮一些文字型別的檔案再返回給使用者。

注意必須手動指定全需要壓縮的型別,比如css、js之類的,線上配置如下:

gzip on;
gzip_min_length  2048;
gzip_buffers     4 16k;
gzip_vary   on;
gzip_http_version 1.1;
gzip_types  text/plain  text/css text/xml application/xml application/javascript application/x-javascript ;

2.12 mime配置

很久以前基本是忽略這個配置,但手遊流行之後就發現異常了,需要讓手機瀏覽器知道返回的apk字尾是什麼型別,否則類似IE瀏覽器會以zip字尾返回,需要加上:

application/vnd.android.package-archive apk;
application/iphone pxl ipa;

2.13 限速

限速包括限制請求的併發數和請求的下載速度。

簡單的限制某個執行緒的下載速度就直接加上一句話就可以了:

limit_rate 1024k;

要限制某個IP的併發數之類的就需要用ngx_http_limit_req_module和ngx_http_limit_conn_module模組了,不過是預設就編譯好的。

比如使用一個 10M 大小的狀態快取區,針對每個IP每秒只接受20次的請求:

limit_req_zone $binary_remote_addr zone=NAME:10m rate=20r/s;

2.14 location匹配

location匹配有多種方式,常見的比如

location  = / 
location  / 
location ^~ /test{

是有優先順序的,直接 ”=” 的優先順序是最高的,一般就用”~”這個符號來匹配php就好了,不過是區分了大小寫的:

location ~ .*\.php$

2.15 檔案快取

返回給使用者的檔案一般都配置了過期時間,讓瀏覽器快取起來。

比如快取14天:

expires 14d;

針對某些特殊的檔案就需要location匹配之後進行禁止快取配置:

add_header Cache-Control no-cache;
add_header Cache-Control no-store;
expires off;

2.16 快取檔案

nginx可以作為ATS這樣的快取伺服器來快取檔案,配置也比較簡單,不過我們很少用,除非一些特殊的場合,參考配置:

#先在全域性下面定義好快取存放的目錄
proxy_cache_path  /data/cache/ levels=1:2 keys_zone=cache_one:10m inactive=7d max_size=10g;
proxy_temp_path   /data/cache/proxy_temp_path;
proxy_cache_key   $host$uri$is_args$args;
#然後在server裡面的location匹配好目的檔案,加入下一段即可
proxy_cache cache_one;
proxy_cache_valid 200 304 24h;
proxy_cache_valid any 10m;
proxy_pass https://$host;
proxy_cache_key $host$uri$is_args$args;
add_header  Nginx-Cache "$upstream_cache_status"; 3. 內建模組

3. 內建模組

nginx含有大量的模組可以支援多種複雜的需求,比如原始碼目錄src/http/modules裡面就有很多c模組的程式碼,或者直接通過./configure –help|grep module來檢視有哪些內建模組,編譯時候直接加上就可以了。

除了nginx內建的模組,網路上還有很多第三方的模組,可以通過編譯時候加引數–add-module=PATH指定模組原始碼來編譯。

下面介紹一些我們線上用過而且比較讚的內建模組。

3.1 stream

埠轉發的模組,從nginx1.9版本才開始支援,包含tcp和udp的支援,和IPTABLES相比這個雖然是應用層,會監聽埠,但是配置起來很方便,比IPTABLES靈活,在tcp模組下面新增類似vhost的server就可以了,方便自動化管理,參考配置:

server {
    listen PORT;
    proxy_pass IP:PORT;
    access_log /data/logs/tcp/PORT.log;
}

3.2 http_realip_module

nginx反向代理之後,如何讓後端web直接獲取到的IP不是反向代理的iP,而是直接獲取到使用者的真實IP呢,就需要這個模組了,不需要程式碼那裡再做類似X-Real-IP的變數特殊判斷。

3.3 http_slice_module

在做CDN時候可以用到,讓一個大檔案分片,分成多個小檔案通過206斷點續傳到後端,然後再組合起來,避免大檔案直接回源導致多副本和多次回源的問題。

3.4 http_secure_link_module

前面說到的防盜鏈可以用這個來做,但是這個一般是針對那種檔案下載時候用到的,比如從網頁下載時候,服務端生成一個加密URL給使用者,然後這個URL有過期時間之類的,避免此URL被多次分享出去,不過普通的素材載入還是用普通的防盜鏈即可。

3.5 http_sub_module

替換響應給使用者的內容,相對於sed之後再返回,比如可以在需要臨時全域性修改網站背景或者title時候可以一次性處理好。

4. 擴充套件專案

簡單介紹下大名鼎鼎的兩個基於nginx的擴充套件專案,也是我們線上有很多地方用到的。

4.1 openresty

整合lua指令碼,幾乎可以完成任何普通web相關的需求。

比如URL加密進行防劫持和防盜鏈,服務端動態生成一串aes加密的URL給CDN,CDN的openresty解密之後用普通的URL轉發到後端,然後再返回給使用者正確的內容。

4.2 tengine

淘寶的nginx修改版,實現了很多nginx的收費功能或者是特殊功能,比如動態載入、concat合併請求,動態解析等。

我們python開發的後臺基本都是用的這個版本,主要是利用了concat的合併素材的功能。

5. 結語

Nginx是個非常實用軟體,部分功能已經超越了普通的web服務定位,同時它具備開源、輕量、自動化等特性,能有效解決實際工作中很多特殊場景的需求,祝Nginx在全球的份額持續攀升~

相關文章