1. 程式人生 > 其它 >你真的瞭解如何將 Nginx 配置為Web伺服器嗎

你真的瞭解如何將 Nginx 配置為Web伺服器嗎

閱讀之前,建議先閱讀初識 Nginx。 之後,我們來了解一下 Nginx 配置。

抽象來說,將 Nginx 配置為 Web 伺服器就是定義處理哪些 URLS 和如何處理這些URLS 對應的請求。具體來說,就是定義一些虛擬伺服器(Virtual Servers),控制具有特定 IP 和域名的請求。

更具體的來說, Nginx 通過定義一系列 locations 來控制對 URIS 的選擇。每一個 location 定義了對對映到自己的請求的處理場景:返回一個檔案或者代理請求,或者根據不同的錯誤程式碼返回不同的錯誤頁面。另外,根據 URI 的不同,請求也可以被重定向到其它 server 或者 location

設定虛擬伺服器

listen

Nginx 配置檔案至少包含一個 server 命令 ,用來定義虛擬伺服器。當請求到來時, Nginx 會首先選擇一個虛擬伺服器來處理該請求。

虛擬伺服器定義在 http 上下文中的 server 中:

http {
    server {
        # Server configuration
    }
}

注意: http 中可以定義多個 server

server 配置塊使用 listen 命令監聽本機 IP 和埠號(包括 Unix domain socket and path),支援 IPv4、IPv6,IPv6地址需要用方括號括起來:

server {
    listen 127.0.0.1:8080;  # IPv4地址,8080埠
    # listen [2001:3CA1:10F:1A:121B:0:0:10]:80;   # IPv6地址,80埠
    # listen [::]:80;  # 聽本機的所有IPv4與IPv6地址,80埠
    # The rest of server configuration
}

上述配置,如果不寫埠號,預設使用80埠,如果不寫 IP ,則監聽本機所有 IP。

server_name

如果多個 server 的 listen IP 和埠號一模一樣, Nginx 通過請求頭中的 Host 

與 server_name

 定義的主機名進行比較,來選擇合適的虛擬伺服器處理請求:

server {
    listen      80;
    server_name lufficc.com  www.lufficc.com;
    ...
}

server_name 的引數可以為:

  1. 完整的主機名,如:api.lufficc.com 。
  2. 含有萬用字元(含有 *),如:*.lufficc.com 或 api.* 。
  3. 正則表示式,以 ~ 開頭。

萬用字元只能在開頭或結尾,而且只能與一個 . 相鄰。www.*.example.org 和 w*.example.org均無效。 但是,可以使用正則表示式匹配這些名稱,例如 ~^www..+.example.org$ 和 ~^w.*.example.org$ 。 而且 * 可以匹配多個部分。 名稱 * .example.org 不僅匹配 www.example.org,還匹配www.sub.example.org。 對於正則表示式:Nginx 使用的正則表示式與 Perl 程式語言(PCRE)使用的正則表示式相容。 要使用正則表示式,且必須以 ~ 開頭。

命名的正則表示式可以捕獲變數,然後使用:

server {
    server_name   ~^(www.)?(?<domain>.+)$;

    location / {
        root   /sites/$domain;
    }
}

小括號 () 之間匹配的內容,也可以在後面通過 $1 來引用,$2 表示的是前面第二個 () 裡的內容。因此上述內容也可寫為:

server {
    server_name   ~^(www.)?(.+)$;

    location / {
        root   /sites/$2;
    }
}

一個 server_name 示例:

server {
    listen      80;
    server_name api.lufficc.com  *.lufficc.com;
    ...
}

同樣,如果多個名稱匹配 Host 頭部, Nginx 採用下列順序選擇:

  1. 完整的主機名,如 api.lufficc.com
  2. 最長的,且以 * 開頭的通配名,如:*.lufficc.com
  3. 最長的,且以 * 結尾的通配名,如:api.* 。
  4. 第一個匹配的正則表示式。(按照配置檔案中的順序)

即優先順序:api.lufficc.com > *.lufficc.com > api.* > 正則。

如果 Host 頭部不匹配任何一個 server_name ,Nginx 將請求路由到預設虛擬伺服器。預設虛擬伺服器是指:nginx.conf 檔案中第一個 server 或者 顯式用 default_server 宣告:

server {
    listen      80 default_server;
    ...
}

配置 location

URI 與 location 引數的匹配

當選擇好 server 之後,Nginx 會根據 URIs 選擇合適的 location 來決定代理請求或者返回檔案。

location 指令接受兩種型別的引數:

  1. 字首字串(路徑名稱)
  2. 正則表示式

對於字首字串引數, URIs 必須嚴格的以它開頭。例如對於 /some/path/ 引數,可以匹配 /some/path/document.html ,但是不匹配 /my-site/some/path,因為 /my-site/some/path 不以 /some/path/ 開頭。

location /some/path/ {
    ...
}

對於正則表示式,以 ~ 開頭表示大小寫敏感,以 ~* 開頭表示大小寫不敏感。注意路徑中的 . 要寫成 . 。例如一個匹配以 .html 或者 .htm 結尾的 URI 的 location

location ~ .html? {
    ...
}

正則表示式的優先順序大於字首字串。如果找到匹配的字首字串,仍繼續搜尋正則表示式,但如果字首字串以 ^~ 開頭,則不再檢查正則表示式。

具體的搜尋匹配流程如下:

  1. 將 URI 與所有的字首字串進行比較。
  2. = 修飾符表明 URI 必須與字首字串相等(不是開始,而是相等),如果找到,則搜尋停止。
  3. 如果找到的最長字首匹配字串以 ^~ 開頭,則不再搜尋正則表示式是否匹配。
  4. 儲存匹配的最長字首字串。
  5. 測試對比 URI 與正則表示式。
  6. 找到第一個匹配的正則表示式後停止。
  7. 如果沒有正則表示式匹配,使用 4 儲存的字首字串對應的 location

= 修飾符擁有最高的優先順序。如網站首頁訪問頻繁,我們可以專門定義一個 location 來減少搜尋匹配次數(因為搜尋到 = 修飾的匹配的 location 將停止搜尋),提高速度:

location = / {
    ...
}

靜態檔案和代理

location 也定義瞭如何處理匹配的請求:返回靜態檔案 或者 交給代理伺服器處理。下面的例子中,第一個 location 返回 /data 目錄中的靜態檔案,第二個 location 則將請求傳遞給 https://lufficc.com 域名的伺服器處理:

server {
    location /images/ {
        root /data;
    }

    location / {
        proxy_pass https://lufficc.com;
    }
}

root 指令定義了靜態檔案的根目錄,並且和 URI 拼接形成最終的本地檔案路徑。如請求 /images/example.png,則拼接後返回本地伺服器檔案 /data/images/example.png 。

proxy_pass 指令將請求傳遞到 URL 指向的代理伺服器。讓後將來自代理伺服器的響應轉發給客戶端。 在上面的示例中,所有不以 /images / 開頭的 URI 的請求都將傳遞給代理伺服器處理。

比如我把 proxy_pass 設定為 https://www.baidu.com/,那麼訪問 http://search.lufficc.com/ 將得到百度首頁一樣的響應(頁面)(感興趣的童鞋可以自己試一試搜尋功能,和百度沒差別呢):

server{
      listen 80;
      server_name search.lufficc.com;
      location / {
              proxy_pass https://www.baidu.com;
      }
}

使用變數(Variables)

你可以使用變數來使 Nginx 在不同的請求下采用不同的處理方式。變數是在執行時計算的,用作指令的引數。 變數由 $ 開頭的符號表示。 變數基於 Nginx 的狀態定義資訊,例如當前處理的請求的屬性。

有很多預定義變數,例如核心的 HTTP 變數,你也可以使用 setmap 和 geo 指令定義自定義變數。 大多數變數在執行時計算,幷包含與特定請求相關的資訊。 例如,$remote_addr 包含客戶端 IP 地址,$uri 儲存當前URI值。

一些常用的變數如下:

變數名稱

作用

$uri

請求中的當前URI(不帶請求引數),它可以通過內部重定向,或者使用index指令進行修改,$uri不包含主機名,如 /foo/bar.html。

$arg_name

請求中的的引數名,即“?”後面的arg_name=arg_value形式的arg_name

$hostname

主機名

$args

請求中的引數值

$query_string

同 $args

$request

代表客戶端的請求地址

$request_uri

這個變數等於包含一些客戶端請求引數的原始URI,它無法修改,不包含主機名,如:/cnphp/test.php?arg=freemouse。

...

...

一個簡單的應用就是從 http 重定向到 https 時帶上路徑資訊:

server{
       ...
       return      301 https://lufficc.com$request_uri;
       ...
}

返回特定狀態碼

如果你的網站上的一些資源永久移除了,最快最簡潔的方法就是使用 return 指令直接返回:

location /wrong/url {
    return 404;
}

return 的第一個引數是響應程式碼。可選的第二個引數可以是重定向(對應於程式碼301,302,303和307)的 URL 或在響應正文中返回的文字。 例如:

location /permanently/moved/url {
    return 301 http://www.example.com/moved/here;
}

return 指令可以包含在 location 和 server 上下文中:

server{
      location / {
              return 404;
      }
}

或者:

server{
      ...
      return 404;
      location / {
          ...            
      }
}

錯誤處理

error_page 命令可以配置特定錯誤碼的錯誤頁面,或者重定向到其他的頁面。下面的示例將在 404 錯誤發生時返回 /404.html 頁面。

error_page 404 /404.html;

error_page 命令定義瞭如何處理錯誤,因此不會直接返回,而 return 確實會立即返回。當代理伺服器或者 Nginx 處理時產生相應的錯誤的程式碼,均會返回相應的錯誤頁面。

在下面的示例中,當 Nginx 找不到頁面時,它將使用程式碼301替換程式碼404,並將客戶端重定向到 http://example.com/new/path.html 。 此配置很有用,比如當客戶端仍嘗試用舊的 URI 訪問頁面時,301程式碼通知瀏覽器頁面已永久移除,並且需要自動替換為返回的新地址。

location /old/path.html {
    error_page 404 =301 http:/example.com/new/path.html;
}

重寫 URIs

rewrite 指令可以多次修改請求的 URIrewrite 的第一個引數是 URI需要匹配的正則表示式,第二個引數是將要替換的 URI。第三個引數可選,指示是否繼續可以重寫或者返回重定向程式碼(301或302)。例如:

location /users/ {
    rewrite ^/users/(.*)$ /show?user=$1 break;
}

您可以在 server 和 location 上下文中包括多個 rewrite 指令。 Nginx 按照它們發生的順序一個一個地執行指令。 當選擇 server 時,server 中的 rewrite 指令將執行一次。

在 Nginx 處理一組 rewrite 指令之後,它根據新的 URI 選擇 location 。 如果所選 location仍舊包含 rewrite 指令,它們將依次執行。 如果 URI 匹配所有,則在處理完所有定義的 rewrite 指令後,搜尋新的 location 。

以下示例將 rewrite 指令與 return 指令結合使用:

server {
    ...
    rewrite ^(/download/.*)/media/(.*)..*$ $1/mp3/$2.mp3 last;
    rewrite ^(/download/.*)/audio/(.*)..*$ $1/mp3/$2.ra  last;
    return  403;
    ...
}

諸如 /download/some/media/file 的 URI 被改為 /download/some/mp3/file.mp3 。 由於 last 標誌,後續指令(第二個 rewrite 指令和 return 指令)被跳過,但 Nginx 繼續以更改後的 URI 處理請求。 類似地,諸如 /download/some/audio/file 的 URI 被替換為 /download/some/mp3/file.ra。 如果 URI 不匹配 rewrite 指令,Nginx 將403 錯誤程式碼返回給客戶端。

last 與 break的區別是:

  • last : 在當前 server 或 location 上下文中停止執行 rewrite 指令,但是 Nginx 繼續搜尋與重寫的URI匹配的 location,並應用新 location 中的任何 rewrite 指令(這意味著 URI 可能再次改變)。
  • break :停止當前上下文中 rewrite 指令的處理,並取消搜尋與新 URI 匹配的 location。 不會執行新 location中的 rewrite 指令。

附錄

常用正則

  • . : 匹配除換行符以外的任意字元
  • ? : 重複0次或1次
  • + : 重複1次或更多次
  • *: 重複0次或更多次
  • d :匹配數字
  • ^ : 匹配字串的開始
  • $ : 匹配字串的結束
  • {n} : 重複n次
  • {n,} : 重複n次或更多次
  • [c] : 匹配單個字元c
  • [a-z]: 匹配a-z小寫字母的任意一個

全域性變數

  • $args : #這個變數等於請求行中的引數,同$query_string
  • $content_length : 請求頭中的Content-length欄位。
  • $content_type : 請求頭中的Content-Type欄位。
  • $document_root : 當前請求在root指令中指定的值。
  • $host : 請求主機頭欄位,否則為伺服器名稱。
  • $http_user_agent : 客戶端agent資訊
  • $http_cookie : 客戶端cookie資訊
  • $limit_rate : 這個變數可以限制連線速率。
  • $request_method : 客戶端請求的動作,通常為GET或POST。
  • $remote_addr : 客戶端的IP地址。
  • $remote_port : 客戶端的埠。
  • $remote_user : 已經經過Auth Basic Module驗證的使用者名稱。
  • $request_filename : 當前請求的檔案路徑,由root或alias指令與URI請求生成。
  • $scheme : HTTP方法(如http,https)。
  • $server_protocol : 請求使用的協議,通常是HTTP/1.0或HTTP/1.1。
  • $server_addr : 伺服器地址,在完成一次系統呼叫後可以確定這個值。
  • $server_name : 伺服器名稱。
  • $server_port : 請求到達伺服器的埠號。
  • $request_uri : 包含請求引數的原始URI,不包含主機名,如:/foo/bar.php?arg=baz
  • $uri : 不帶請求引數的當前URI,$uri不包含主機名,如/foo/bar.html
  • $document_uri : 與$uri相同。

例如請求:http://localhost:88/test1/test2/test.php $hostlocalhost $server_port:88 $request_uri/test1/test2/test.php $document_uri/test1/test2/test.php $document_root/var/www/html $request_filename/var/www/html/test1/test2/test.php

參考

  1. https://www.nginx.com/resources/admin-guide/nginx-web-server/
  2. http://seanlook.com/2015/05/17/nginx-location-rewrite/

-- END