home > tools > webServer > nginx >

Nginx葵花寶典—草根站長配置Nginx運維百科全書

author:zhoulujun    hits:

nginx,也沒有那么容易,先從轉發開始細講,我覺得下面的內容,基本覆蓋了一個資深程序員或運維的日常需求。這里把功能點都做了分類、筆記提示等,不足之處也請道友們補充。

題記

前段時間把網站遷移到騰訊云,之前是lamp,現在改為lnmp,自以為nginx功底還可以,開發這么多年,平常環境都有配置。但是,但是,最近讀站點做SEO優化,發現nginx很多地方不會配。比如:

http://www.qsexmk.tw/

http://www.qsexmk.tw/index.html

http://www.qsexmk.tw/index.php

http://zhoulujun.cn/index.html

https://zhoulujun.cn/index.html

……

這些頁面均為重復頁面,再看

http://www.qsexmk.tw/?a=1&b=2&****

http://www.qsexmk.tw/?index.phpa=1&b=2&****

以及CDN轉發,功能切分多域名轉發,負載均衡,路徑優化,如此等……

nginx,也沒有那么容易,先從轉發開始細講,我覺得下面的內容,基本覆蓋了一個資深程序員或運維的日常需求。這里把功能點都做了分類、筆記提示等,不足之處也請道友們補充。閑暇之余,希望把nginx系統地梳理一遍

timg.jpg

nginx正則表達式在location匹配規則及優先級

  • =   精確匹配        嚴格匹配這個查詢。如果找到,停止搜索

  • ~   正則匹配        為區分大小寫的正則匹配

  • ^~ 優先前綴匹配 匹配路徑的前綴,如果找到,停止搜索

  • ~*  正則匹配        為不區分大小寫匹配 

  • !~和!~*                分別為區分大小寫不匹配及不區分大小寫不匹

  • /                           任何請求都會匹配

優先級: =, ^~, ~/~*, 無

具體可以參考:Nginx Location 路徑匹配優先級

nginx文件及目錄匹配

  • -f和!-f用來判斷是否存在文件

  • -d和!-d用來判斷是否存在目錄

  • -e和!-e用來判斷是否存在文件或目錄

  • -x和!-x用來判斷文件是否可執行


請求URI(路徑)規范化。

所謂規范化,就是先將URI中形如“%XX”的編碼字符進行解碼,再解析URI中的相對路徑“.”和“..”部分, 另外還可能會壓縮相鄰的兩個或多個斜線成為一個斜線。

舉例說明:若REQUEST_URI為//trip/t.php,則規范化后為/trip/t.php,Nginx將規范前的值存放在$request_uri中,而規范化后的值存放在$uri中。

其中,$request_uri和$uri為Nginx內嵌變量。

請求URI路徑匹配

首先需要明確Nginx中將路徑匹配分為兩類:

  • 前綴路徑匹配,即前綴字符串定義的路徑,如上配置文件中“/,/static/js/,/static/css/,/api,/trip/”

  • 正則表達式路徑匹配,即使用正則表達式需要在路徑開始添加“~*”前綴 (不區分大小寫),或者“~”前綴(區分大小寫)。如上配置文件中“/\.ht,^/~([^/]+)(/?.*)$,\.do$,/trip/, \.php$,\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar||bmp|rtf|js|mov)”

其次為了根據請求URI查找路徑,需要明確路徑匹配的順序:

Nginx首先檢查前綴字符串定義的路徑 (前綴路徑),在這些路徑中找到能最精確匹配請求URI的路徑。然后Nginx按在配置文件中的出現順序檢查正則表達式路徑,匹配上某個路徑后即停止匹配并使用該路徑的配置否則使用最大前綴匹配的路徑的配置


舉例說明:請求/trip/t.php,首先進行前綴路徑匹配,最精確的前綴路徑為/trip/,接下來進行正則表達式匹配,匹配到\.php$,從而進行location ~ \.php$ { }處理請求。反之若請求的是/trip/t.html,由于沒有正則表達式匹配到該URI,故匹配最精確的前綴路徑匹配,即進入location /trip/ { }處理請求。若想不論是請求/trip/t.php,還是/trip/t.html,都匹配到/trip/進行處理,則可以使用location ^~ /trip/ { },這樣Nginx就不會再檢查正則表達式了。


Nginx虛擬目錄alias和root目錄

nginx是通過alias設置虛擬目錄,在nginx的配置中,alias目錄和root目錄是有區別的:

  • 1)alias指定的目錄是準確的,即location匹配訪問的path目錄下的文件直接是在alias目錄下查找的;

  • 2)root指定的目錄是location匹配訪問的path目錄的上一級目錄,這個path目錄一定要是真實存在root指定目錄下的;

  • 3)使用alias標簽的目錄塊中不能使用rewrite的break(具體原因不明);另外,alias指定的目錄后面必須要加上"/"符號!!

  • 4)alias虛擬目錄配置中,location匹配的path目錄如果后面不帶"/",那么訪問的url地址中這個path目錄后面加不加"/"不影響訪問,訪問時它會自動加上"/";

  •     但是如果location匹配的path目錄后面加上"/",那么訪問的url地址中這個path目錄必須要加上"/",訪問時它不會自動加上"/"。如果不加上"/",訪問就會失敗!

  • 5)root目錄配置中,location匹配的path目錄后面帶不帶"/",都不會影響訪問。

一般情況下,在nginx配置中的良好習慣是:

  • 1)在location /中配置root目錄;

  • 2)在location /path中配置alias虛擬目錄。


Nginx指令詳解

if指令

使用環境:server,location

該指令用于檢查一個條件是否符合,如果條件符合,則執行大括號內的語句。If指令不支持嵌套,不支持多個條件&&和||處理。

return指令

語法:returncode ;

使用環境:server,location,if;

該指令用于結束規則的執行并返回狀態碼給客戶端

Set指令

語法:setvariable value ; 默認值:none; 使用環境:server,location,if;

該指令用于定義一個變量,并給變量賦值。變量的值可以為文本、變量以及文本變量的聯合。

示例:set$varname "hello world";

Uninitialized_variable_warn指令

語法:uninitialized_variable_warnon|off

使用環境:http,server,location,if

該指令用于開啟和關閉未初始化變量的警告信息,默認值為開啟。

rewrite 指令

語法:rewriteregex replacement flag

使用環境:server,location,if

該指令根據表達式來重定向URI,或者修改字符串。指令根據配置文件中的順序來執行。注意重寫表達式只對相對路徑有效。


rewrite參數 flag標志位

在server塊下,會優先執行rewrite部分,然后才會去匹配location塊 

server中的rewrite break和last沒什么區別,都會去匹配location,所以沒必要用last再發起新的請求,可以留空

location中的rewirte:

不寫last和break - 那么流程就是依次執行這些rewrite 

使用last和break實現URI重寫,瀏覽器地址欄不變

  • break - url重寫后,直接使用當前資源,不再執行location里余下的語句,完成本次請求,地址欄url不變 

  • last - url重寫后,馬上發起一個新的請求,再次進入server塊,重試location匹配,超過10次匹配不到報500錯誤,地址欄url不變。牢記:使用last會對server標簽重新發起請求

使用redirect 和permanent 實現URI重寫,瀏覽器以返回的新地址重新發起請求

  • redirect – 返回302臨時重定向,地址欄顯示重定向后的url,爬蟲不會更新url(因為是臨時) 

  • permanent – 返回301永久重定向, 地址欄顯示重定向后的url,爬蟲更新url

last 和 break 總結如下:

1、last 和 break 當出現在location 之外時,兩者的作用是一致的沒有任何差異

注意一點就是,他們會跳過所有的在他們之后的rewrite 模塊中的指令,去選擇自己匹配的location

        rewrite url1 url2 last; ①

        rewrite url3 url4 last; ②

        rewrite url5 url6 last; ③

        location ~  url2     ④  

        location ~  url4     ⑤

        location ~  url6     ⑥

當① 這條rewrite 規則生效后,它后面的②和③ 將被跳過不做判斷,而去直接選擇 后面的location。

這里可能有一個疑問,那些指令輸入rewrite 模塊中的指令呢? 若是使用nginx本身,你就要到官網上去查詢了。

但如果你使用的是tengine ,可以使用tengine -V 。會將你想要的信息列舉出來。


放在server塊rewrite語句前面 :如果是直接請求某個真實存在的文件,則用break語句停止rewrite檢查 

    if (-f $request_filename) { 

        break; 

    }

2、last 和 break 當出現在location 內部時,兩者就存在了差異。

   last: 使用了last 指令,rewrite 后會跳出location 作用域,重新開始再走一次剛剛的行為break: 使用了break 指令,rewrite后不會跳出location 作用域。它的生命也在這個location中終結。

        rewrite xxx1 yyy last; ⑦

        rewrite xxx2 yyy last; ⑧

        rewrite xxx3 yyy last; ⑨

        rewrite xxx4 yyy last; ⑩

        location ~  url1 {

            rewrite url1 url2 last; ①

        }

        location ~  url2  {

            rewrite url3 url4 break; ②

            fastcgi_pass 127.0.0.1:9000;

        }

以上事例:

第一個location 中的 rewrite 指令處理完成之后,會跳出location ,再重新判斷rewrite 7 ~ 9 的規則。

第二個location 中的 rewrite  指令處理完成之后,不會跳出location, 更不會重新判斷rewrite 7 ~ 9 的規則。而只能將

信息傳遞給后面的fastcgi_pass 或者proxy_pass 等指令

牢記:使用last會對server標簽重新發起請求

  • 如果location中rewrite后是對靜態資源的請求,不需要再進行其他匹配,一般要使用break或不寫,直接使用當前location中的數據源,完成本次請求 

  • 如果location中rewrite后,還需要進行其他處理,如動態fastcgi請求(.php,.jsp)等,要用last繼續發起新的請求

  • 使用alias指定源:必須使用last

  • 使用proxy_pass指令時,需要使用break標記。


permanent 和 redirect 總結如下:

permanent: 大家公認的信息 ,永久性重定向。請求日志中的狀態碼為301

redirect: 大家公認的信息 ,臨時重定向。請求日志中的狀態碼為302

從實現功能的角度上去看,permanent 和 redirect 是一樣的。不存在哪里好,哪里壞。也不存在什么性能上的問題。

但從SEO(或者是百度爬你的網站時)。 類似于這樣的東西,會對你到底是永久性重定向還是臨時重定向感興趣。了解不到,需要深入,就google 吧。

last 和 break VS permanent 和 redirect 

在 permanent 和 redirect  中提到了 狀態碼 301 和 302。 那么last 和 break 想對于的訪問日志的請求狀態碼又是多少呢?

答案為: 200

這兩類關鍵字,我們能夠眼睛看到的差異是什么呢? 我舉個例子說明吧:

當你打開一個網頁,同時打開debug 模式時,會發現301 和 302 時的行為是這樣的。第一個請求301 或者 302 后,瀏覽器重新獲取了一個新的URL ,然后會對這個新的URL 重新進行訪問。所以當你配置的是permanent 和 redirect ,你對一個URL 的訪問請求,落到服務器上至少為2次

而當你配置了last 或者是break 時,你最終的URL 確定下來后,不會將這個URL返回給瀏覽器,而是將其扔給了fastcgi_pass或者是proxy_pass指令去處理。請求一個URL ,落到服務器上的次數就為1次。




nginx內置變量

內置變量存放在  ngx_http_core_module 模塊中,下面我來把這些變量分類記憶下,這里包括日常運維的內置變量講解

nginx地址欄系統內置變量匹配


http://www.qsexmk.tw/index.php?m=content&c=index&a=lists&catid=58的匹配順序為例:

$scheme 請求使用的Web協議, “http” 或 “https”

$host 請求中的主機頭(Host)字段,如果請求中的主機頭不可用或者空,則為處理請求的server名稱(處理請求的server的server_name指令的值)。值為小寫,不包含端口。

$hostname  主機名,機器名使用 gethostname系統調用的值


$document_uri 與$uri相同。請求中的當前URI(不帶請求參數,參數位于$args),可以不同于瀏覽器傳遞的$request_uri的值,它可以通過內部重定向,或者使用index指令進行修改,$uri不包含主機名,如”/foo/bar.html”。

$document_root 當前請求的文檔根目錄或別名——當前請求在root指令中指定的值。


$args 這個變量等于GET請求中的參數。$query_string 與$args相同。例如,foo=123&bar=blahblah;這個變量只可以被修改

$arg_name請求中的的參數名,即“?”后面的arg_name=arg_value形式的arg_name

$is_args 如果$args設置,值為"?",否則為""。


$cookie_COOKIE cookie COOKIE的值。

$cookie_name cookie名稱

nginx服務端參數內置變量匹配


$server_protocol 服務器的HTTP版本, 通常為 “HTTP/1.0” 或 “HTTP/1.1”

$server_nam 服務器名,如www.qsexmk.tw

$server_addr 服務器端地址,需要注意的是:為了避免訪問linux系統內核,應將ip地址提前設置在配置文件中。

$server_port 服務器端口

$status HTTP響應代碼 (1.3.2, 1.2.2)

$https 如果開啟了SSL安全模式,值為“on”,否則為空字符串。

nginx客戶端參數內置變量匹配

$remote_addr 客戶端的IP地址。

$remote_port 客戶端的端口。

$remote_user 用于HTTP基礎認證服務的用戶名,已經經過Auth Basic Module驗證的用戶名。

$request代表客戶端的請求地址

$request_filename 當前連接請求的文件路徑,由root或alias指令與URI請求生成。

$realpath_root當前請求的文檔根目錄或別名的真實路徑,會將所有符號連接轉換為真實路徑。

$request_body 客戶端的請求主體,此變量可在location中使用,將請求主體通過proxy_pass, fastcgi_pass, uwsgi_pass, 和 scgi_pass傳遞給下一級的代理服務器。這個變量(0.7.58+)包含請求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比較有意義。

$request_body_file 客戶端請求主體信息的臨時文件名。將客戶端請求主體保存在臨時文件中。文件處理結束后,此文件需刪除。如果需要之一開啟此功能,需要設置client_body_in_file_only。如果將次文件傳遞給后端的代理服務器,需要禁用request body,即設置proxy_pass_request_body off,fastcgi_pass_request_body off, uwsgi_pass_request_body off, or scgi_pass_request_body off 。

$request_completion 如果請求成功,設為"OK";如果請求未完成或者不是一系列請求中最后一部分則設為空。

$request_method 這個變量是客戶端請求的動作,通常為GET或POST。包括0.8.20及之前的版本中,這個變量總為main request中的動作,如果當前請求是一個子請求,并不使用這個當前請求的動作。


$http_HEADER   HTTP請求頭中的內容,HEADER為HTTP請求中的內容轉為小寫,-變為_(破折號變為下劃線),例如:$http_user_agent(Uaer-Agent的值), $http_referer...;

$http_name匹配任意請求頭字段; 變量名中的后半部分“name”可以替換成任意請求頭字段,如在配置文件中需要獲取http請求頭:“Accept-Language”,那么將“-”替換為下劃線,大寫字母替換為小寫,形如:$http_accept_language即可。

$sent_http_HEADER  HTTP響應頭中的內容,HEADER為HTTP響應中的內容轉為小寫,-變為_(破折號變為下劃線),例如: $sent_http_cache_control, $sent_http_content_type...;


nginx運維及系統狀態內置變量匹配

$nginx_version 當前運行的nginx版本號。

$time_iso8601 服務器時間的ISO 8610格式 (1.3.12, 1.2.7)

$msec 當前的Unix時間戳 (1.3.9, 1.2.6)

$pid工作進程的PID


$limit_rate 用于設置響應的速度限制

$binary_remote_addr 二進制碼形式的客戶端地址。

$body_bytes_sent 傳送頁面的字節數


$connection TCP連接的序列號 (1.3.8, 1.2.5)

$connection_requests TCP連接當前的請求數量 (1.3.8, 1.2.5)

$content_length 請求頭中的Content-length字段。

$content_type 請求頭中的Content-Type字段。


nginx常用配置案例參考



#多目錄轉成參數

abc.domian.com/sort/2 => abc.domian.com/index.php?act=sort&name=abc&id=2

    if ($host ~* (.*)\.domain\.com) {

        set $sub_name $1;

        rewrite ^/sort\/(\d+)\/?$ /index.php?act=sort&cid=$sub_name&id=$1 last;

    }

#目錄對換

/123456/xxxx -> /xxxx?id=123456

        rewrite ^/(\d+)/(.+)/ /$2?id=$1 last;

#ie用戶使用重定向到/nginx-ie目錄下:

    if ($http_user_agent ~ MSIE) {

        rewrite ^(.*)$ /nginx-ie/$1 break;

    }

#目錄自動加“/”

    if (-d $request_filename){

        rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent;

    }

#禁止多個目錄

    location ~ ^/(cron|templates)/ {

        deny all;

        break;

    }

#錯頁面如40x.html,50x.html設置

    error_page  404 403  /40x.html;

    # 承接上面的location。

    location = /40x.html {

    # 放錯誤頁面的目錄路徑。

        root  /data/wwwroot/zhoulujun/;


    }

#只充許固定ip訪問網站,并加上密碼

        root  /opt/htdocs/www;

        allow   208.97.167.194;

        allow   222.33.1.2;

        allow   231.152.49.4;

        deny    all;

        auth_basic “C1G_ADMIN”;

        auth_basic_user_file htpasswd;

#將多級目錄下的文件轉成一個文件,增強seo效果

/job-123-456-789.html 指向/job/123/456/789.html

        rewrite ^/job-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /job/$1/$2/jobshow_$3.html last;

        

#域名跳轉

    server {

        listen       80;

        server_name  jump.88dgw.com;

        index index.html index.htm index.php;

        root  /opt/lampp/htdocs/www;

        rewrite ^/ http://www.88dgw.com/;

        access_log  off;

    }

#去掉php頁面

    if ($request_uri ~* "^(.*/)index\.php$") {

        return 301 $1;

    }

#index跳轉到域名下

    location  /index.html {

        root /;

        rewrite ^/index.html$ / permanent;

    }

# Remove trailing slash. 去除末尾斜杠

    if (!-d $request_filename) {

        rewrite ^/(.+)/$ /$1 permanent;

    }

# Clean Double Slashes

    if ($request_uri ~* "\/\/") {

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

    }

# 舊站資源轉發,移除一個zhoulun目錄 

    location  /zhoulujun/html/ {

        root /;

        rewrite ^/zhoulujun/html/(.*)$ /html/$1 permanent;

    }

    location  /zhoulujun/uploadfile/ {

        root /;

        rewrite ^/zhoulujun/uploadfile/(.*)$ /uploadfile/$1 break;

    }

#多域名轉向

    server {

        server_name  www.7oom.com/  www.divmy.com/;

        index index.html index.htm index.php;

        root  /opt/lampp/htdocs;

        if ($host ~ “c1gstudio\.net”) {

            rewrite ^(.*) http://www.7oom.com$1/ permanent;

        }

    }

#三級域名跳轉

        if ($http_host ~* “^(.*)\.i\.c1gstudio\.com$”) {

            rewrite ^(.*) http://top.88dgw.com$1/;

            break;

        }

#域名鏡向

    server {

        listen       80;

        server_name  mirror.c1gstudio.com;

        index index.html index.htm index.php;

        root  /opt/lampp/htdocs/www;

        rewrite ^/(.*) http://www.divmy.com/$1 last;

        access_log  off;

    }

#某個子目錄作鏡向

        location ^~ /zhaopinhui {

        rewrite ^.+ http://zph.divmy.com/ last;

        break;


#正則匹配 

    location  = / {

      # 精確匹配 / ,主機名后面不能帶任何字符串

      [ configuration A ]

    }

    

    location  / {

      # 因為所有的地址都以 / 開頭,所以這條規則將匹配到所有請求

      # 但是正則和最長字符串會優先匹配

      [ configuration B ]

    }

    

    location /documents/ {

      # 匹配任何以 /documents/ 開頭的地址,匹配符合以后,還要繼續往下搜索

      # 只有后面的正則表達式沒有匹配到時,這一條才會采用這一條

      [ configuration C ]

    }

    

    location ~ /documents/Abc {

      # 匹配任何以 /documents/Abc 開頭的地址,匹配符合以后,還要繼續往下搜索

      # 只有后面的正則表達式沒有匹配到時,這一條才會采用這一條

      [ configuration CC ]

    }

    

    location ^~ /images/ {

      # 匹配任何以 /images/ 開頭的地址,匹配符合以后,停止往下搜索正則,采用這一條。

      [ configuration D ]

    }

    

    location ~* \.(gif|jpg|jpeg)$ {

      # 匹配所有以 gif,jpg或jpeg 結尾的請求

      # 然而,所有請求 /images/ 下的圖片會被 config D 處理,因為 ^~ 到達不了這一條正則

      [ configuration E ]

    }

    

    location /images/ {

      # 字符匹配到 /images/,繼續往下,會發現 ^~ 存在

      [ configuration F ]

    }

    

    location /images/abc {

      # 最長字符匹配到 /images/abc,繼續往下,會發現 ^~ 存在

      # F與G的放置順序是沒有關系的

      [ configuration G ]

    }

    

    location ~ /images/abc/ {

      # 只有去掉 config D 才有效:先最長匹配 config G 開頭的地址,繼續往下搜索,匹配到這一條正則,采用

        [ configuration H ]

    }


參考文章:

最新版 nginx內置變量 大全

Nginx 中last和break 及 permanent 和 redirect 的愛恨情仇

nginx rewrite規則

Nginx路徑匹配規則詳解

Nginx虛擬目錄alias和root目錄

Nginx Location 路徑匹配優先級

轉載本站文章《Nginx葵花寶典—草根站長配置Nginx運維百科全書》, 請注明出處:http://www.qsexmk.tw/html/tools/webServer/nginx/2018_0618_8124.html