將 nginx 配置為一個 web 服務器

編譯自:
nginx-web-server

目錄

  • 建立虛擬服務器
  • 配置 location
  • 使用變量
  • 返回指定的狀態碼
  • URI 重寫
  • 對 HTTP Response 進行重寫
  • 處理錯誤

這里有一個視頻,有興趣可以看看:nginx 安裝與調優,其中講述了以下主題:

  • nginx 怎樣使你的應用能更快響應、具有更好的伸縮性、更快、更安全?
  • 如何安裝 nginx
  • 為 nginx 調整操作系統參數

從高層次來說,配置 nginx 為一個 web 服務器,主要是定義:

  • 對哪些 URL 進行處理
  • 如何處理對于這些 URL 的 HTTP 請求

從較低層次來說,主要是定義一組 虛擬服務器 ,讓這些 虛擬服務器 去處理對于 特定域名IP地址 的請求。

更多關于如何配置 nginx 的信息,可參考 Creating NGINX Plus Configuration Files

每個 虛擬服務器 都定義了 location 區塊,location 區塊定義了對于指定的一組 URI 是如何進行處理的。

在 location 中可作如下定義:

  • 轉發請求到后端服務器(也就是將此 nginx 服務器作為代理),或者返回一個文件;
  • 對 URI 進行重寫,將請求重定向至其它的 location 或者其它虛擬服務器;
  • 返回指定的 error code,并且可以為每個 error code 配置一個指定的 error page。

建立虛擬服務器


在 nginx 配置文件中,必須包含至少一個 server 指令,server 指令定義了一個虛擬服務器。當一個請求到來時,nginx 首先要決定選擇哪一個虛擬服務器處理該請求。

server 指令在 http context 中進行定義,例如:

http {
    server {
        # Server configuration
    }
}

http context 中,可添加多個 server 指令以定義多個虛擬服務器。

server 區塊中,一般會包含一個 listen 指令。listen 指令是用于定義該虛擬服務器監聽于哪個 IP地址端口(或者定義一個 Unix socket 文件及路徑)。IP地址可為 IPv4 或 IPv6 地址;IPv6 地址應放入方括號中。

以下是一個例子,該服務器監聽于 127.0.0.1 和 8080 端口:

server {
    listen 127.0.0.1:8080;
    # The rest of server configuration
}

如果不指定端口地址,將使用標準的端口地址,即 80 端口。如果不指定 IP 地址,將監聽于所有 IP 地址之上。如果 server 區塊中沒有包含 listen 指令,根據超級用戶的權限,將使用 標準的 80/tcp 端口或者 默認的 8000/tcp 端口。

如果同時有多個 虛擬主機 匹配了請求的IP地址和端口,nginx 將測試請求的 Host 首部字段,將它與 server 區塊中的 server_name 指令的配置進行對比。server_name 指令的值有三種:準確的主機名通配主機名正則表達式主機名。通配主機名在其起始、或末尾有一個 * 符號,或者起始和末尾都有一個 * 符號,* 可匹配任意長度的字符串;nginx 的正則表達式兼容 Perl 的正則語法,在正則表達式之前需有~前綴。

以下是關于 準確的主機名 的例子:

server {
    listen      80;
    server_name example.org www.example.org;
    ...
}

如果有多個 server_name 指定的主機名與請求首部的 Host 字段匹配,nginx 按如下的優先次序選擇,當找到第一個能匹配的 server name 就終止查找:

  1. 準確的主機名
  2. 以 “*” 起始的最長的通配主機名,比如 *.example.org
  3. 以 “*” 結尾的最長的通配主機名,比如 mail.*
  4. 第一個匹配的正則表達式(按照配置文件中的順序)

如果沒有找到與 Host 字段匹配的 server name,nginx 將把請求路由到該端口地址的 默認的虛擬主機。 如果沒有特別指定,默認的虛擬主機 就是 nginx.conf 文件中的第一個定義的 虛擬主機。也可以使用 listen 指令的 default_server 參數顯式地指定一個虛擬主機為 默認的虛擬主機

server {
    listen      80 default_server;
    ...
}

配置 location


nginx 可基于請求的 URI,將訪問流量轉發給不同的后端服務器,或者將不同的文件提供給客戶端。這個功能由 location 指令定義,location 指令在 server 區塊中定義。

舉個例子,你可以定義三個 location 區塊,將一些請求轉發給一個后端服務器,將另一些請求轉發給另一個不同的后端服務器,余下的請求從本地的文件系統中提供客戶端所請求的文件。

當一個請求交給了某個虛擬主機之后,nginx 將請求的 URI 與該虛擬主機內所有的 location 指令的定義進行對比,然后將請求映射到匹配的 location 之中。

在每個 location 區塊中,一般可以放置更多的 location 指令(有少數例外),用于進一步對請求進行分組處理。

Note: 在本文中,location 這個詞只表示一個單獨的 location 上下文。

location 指令可定義兩種參數:

  • 前綴字符串(路徑名)
  • 正則表達式

如果一個請求的 URI 以某個 前綴字符串 為起始,則與該 location 匹配。

下面舉一個 前綴字符串 的例子,在下面的例子中,該 location 的參數為 /some/path/ ,它能匹配類似于 URI 為 /some/path/document.html 的請求:

location /some/path/ {
    ...
}

正則表達式必須帶有前綴 ~~*(忽略大小寫)。在下面的例子中,該 location 可匹配包含 .html 或者 .htm 的請求:

location ~ \.html? {
    ...
}

為了找到某個 URI 的最佳匹配,nginx 首先比較 URI 和 前綴字符串,然后比較 URI 和 正則表達式。

正則表達式擁有更高的優先級,除非正則表達式使用了 ^~ 修飾符。

在所有匹配的 前綴字符串 中,nginx 選擇其中最長及最完整的前綴字符串所對應的 location。

下面是準確的查找 loction 的過程:

  1. 將 URI 與所有的 前綴字符串 進行比較
  2. 修飾符 = 表示 URI 與 前綴字符串 必須精確匹配。如果能夠精確匹配,則結束查找
  3. 如果在匹配的最長的 前綴字符串 前添加了 ^~ 修飾符,則不會再檢查正則表達式
  4. 保存匹配的最長的 前綴字符串
  5. 測試 URI 和 正則表達式的匹配情況
  6. 一旦找到匹配的正則表達式,則終止查找,并使用對應的 location
  7. 如果沒有匹配的正則表達式,使用保存匹配的最長的 前綴字符串

如果對于 / 的請求很頻繁,可為 location / 添加 = 修飾符,這樣可以加速處理過程,因為一次匹配查找即可結束:

location = / {
    ...
}

在 location 區塊中,可匹配將請求轉發給后端服務器。在下面的例子中,對于匹配第二個 location 的請求,將被轉發給 www.example.com

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

    location / {
        proxy_pass http://www.example.com;
    }
}

root 指令指定了提供靜態文件的本地路徑。如果請求的 URI 匹配第一個 location,將 URI 追加到 /data 之后即可得到文件的訪問路徑:/data/URI。比如請求的 URI 為 /images/example.png,nginx 將 /data/images/example.png 文件返回給客戶端。

proxy_pass 指令用于將請求轉發給后端服務器,參數值即為后端服務器的訪問路徑 URL。從后端服務器取回響應之后,nginx 再轉發給客戶端。在上面的例子中,所有不以 /images/ 起始的 URI,對應的請求將被轉發給后端服務器。

使用變量


nginx.conf 中可使用變量。變量的值在運行時進行計算,可作為指令的參數使用。引用變量,使用 $ 符號,比如 $remote_addr。變量的定義信息依賴于 nginx 當前的狀態,比如當前被處理的請求的屬性等。

有許多變量是預定義的,比如 core HTTP 定義的變量;另外你也可以自定義變量,使用 setmap,或 geo 指令進行自定義。

大多數變量的值是在運行時計算出來的,其中包含特定請求的信息。比如 $remote_addr 包含客戶端的 IP地址 信息,而 $uri 包含當前的 URI 值。

返回指定的狀態碼


在有些情況下,比如當一個頁面已經被臨時或永久地移動到其他位置,訪問這種 web 站點的 URI 時需要立即返回一個錯誤碼或者重定向碼。最容易的方法是使用 return 指令:

location /wrong/url {
    return 404;
}

return 指令的第一個參數是響應狀態碼。第二參數是可選的,可以是一個重定向的 URL(for codes 301, 302, 303, and 307),或者是放入響應 body 的一段文本。比如:

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

return 指令可放入 location 和 server 上下文中。

URI 重寫


在處理請求的過程中,使用 rewrite 指令可對 URI 進行多次修改。

rewrite 指令的語法為:

rewrite regex replacement [flag]

regex 是用于匹配 URI 的正則表達式,replacement 參數用于替換匹配的 URI。flag 參數是可選的,可用于終止進一步的 rewrite 指令操作,或者發送重定向(301 或 302)給客戶端,例如:

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

server 上下文和 location 上下文中,都可包含多個 rewrite 指令。nginx 按照 rewrite 指令出現的順序依次進行處理。當一個 server 上下文被選擇用于處理請求時,在 server 上下文中的 rewirte 指令被執行一次。

當 nginx 對一組 rewrite 指令進行處理之后,它根據新的 URI 選擇匹配的 location 上下文。如果在選擇的 location 中也包含 rewrite 指令,它們將被依次執行。如果 URI 匹配其中任意一個,當所有定義的 rewrite 指令被處理之后,開始對新的 URI 進行查找。

如下例子中,rewrite 指令和 return 指令聯合使用:

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

上例中,對兩組 URI 進行了區分。

  • 類似于 /download/some/media/file 的 URI 請求被替換為 /download/some/mp3/file.mp3。因為該 rewrite 指令添加了 last flag,它將跳過后面的指令(第二個 rewrite 指令和 return 指令)。然后 nginx 開始處理新的 URI。

  • 類似于 /download/some/audio/file 的 URI 請求被替換為 /download/some/mp3/file.ra。因為該 rewrite 指令添加了 last flag,它將跳過后面的指令(return 指令)。

  • 如果 URI 不匹配 location 中的任何一個 rewrite 指令,nginx 返回 403 狀態碼給客戶端。

rewrite 指令有兩個參數可以終止 rewrite 指令集的處理:

  • last - 停止當前 server 或 location 上下文中的 rewrite 指令集的執行,nginx 對修改后的 URI 進行匹配查找。如果新匹配的 location 中包含 rewrite 指令,URI 有可能被再次修改。

  • break - 如同 break 指令,終止當前上下文中的 rewrite 指令集的執行,nginx 對修改后的 URI 進行匹配查找,在新匹配的 location 中如果包含 rewrite 指令,它們不會被執行。

對 HTTP Response 進行重寫


有時你需要對 HTTP 響應報文的內容進行重寫或修改。你可以使用 sub_filter 指令來定義重寫操作。該指令支持變量和鏈式替換,可作復雜的修改:

舉個例子,你可以對指向一個服務器的絕對鏈接進行修改:

location / {
    sub_filter      /blog/ /blog-staging/;
    sub_filter_once off;
}

另一個例子,修改 http://https:// ,并且將 localhost 地址修改為請求首部中的 host 字段的值。sub_filter_once 指令告訴 nginx 是否對所有匹配的字符串重復地執行替換操作,還是只在第一次遇到匹配的字符串時,執行一次替換操作(只替換一次)。sub_filter_once 默認為 on,表示只在第一次遇到匹配的字符串時,替換一次:

location / {
    sub_filter     'href="https://$host/';
    sub_filter     'img src="http://127.0.0.1:8080/' 'img src="https://$host/';
    sub_filter_once on;
}

注意,如果響應報文的一部分已經被一個 sub_filter 指令所修改,當另一個 sub_filter 也匹配該部分時,不會再進行修改。

處理錯誤


使用 error_page 指令,你可以配置 nginx :

  • 返回一個 error code 以及一個自定制的頁面;
  • 在響應報文中,替換一個不同的 error code
  • 發送重定向指令給瀏覽器,指向不同的 URI

在下面的例子中,error_page 指令指定了 /404.html 頁面,這個頁面在返回 404 code 時會一并返回給客戶端:

error_page 404 /404.html;

要注意的是,這個指令不是立即返回該錯誤給客戶端(這是 return 指令做的事),這只是定義了如果處理該錯誤。該 error code 可來自于一個后端服務器,或者在 nginx 做一些處理時出現(例如,當 nginx 不能找到請求的文件時,將返回 404 錯誤)。

下面的例子中,當 nginx 找不到請求的頁面時,它將 code 404 替換為了 code 301,并將返回一個重定向給客戶端:http:/example.com/new/path.html

這個配置是很有用的,當客戶端嘗試以一個老舊的 URI 請求頁面時,可返回重定向指令,令其訪問在新路徑下的文件。

code 301 告訴瀏覽器,該頁面已經被永久地轉移到了其他地方,它需要將返回的新的地址替換原來的老的地址:

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

下面的例子中,當請求 /images/ 路徑下的文件發生文件未找到的錯誤時,通過內部重定向轉發到后端服務器。因為在 error_page 指令中,在 = 后面沒有定義替換的狀態碼,所以返回給客戶端的狀態碼由后端服務器指定(不一定是 404):

server {
    ...
    location /images/ {
        # Set the root directory to search for the file
        root /data/www;

        # Disable logging of errors related to file existence
        open_file_cache_errors off;

        # Make an internal redirect if the file is not found
        error_page 404 = /fetch$uri;
    }

    location /fetch/ {
        proxy_pass http://backend/;
    }
}

error_page 指令告訴 nginx ,當發生 file not found 錯誤時,做一個內部的重定向。$uri 變量的值為當前請求的 URI,在重定向中被轉發給了后端服務器。

例如,如果 /images/some/file 未找到,它被替換為 /fetch/images/some/file,對這個新 URI,nginx 會查找與其匹配的 location,這里就是 location /fetch/,然后就被轉發給后端服務器了。
(可參考 反向代理

當發送 file not found 錯誤時,open_file_cache_errors 指令在這里用于防止記錄錯誤日志。因為請求已經被轉發給后端,所以不應該記錄錯誤日志。


版權信息
*本文編譯自 nginx.com

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容