将 Nginx 配置为 Web 服务器,实际上就是指明服务器处理哪些 HTTP 请求(用 URL 区分)。需要在配置文件中定义虚拟服务器,用于处理对特定域名或 IP 地址的请求。
针对 HTTP 流量的每个虚拟服务器都要通过定义 location 来处理特定的 URI 请求或返回文件。另外,可以修改 URI,以便将请求重定向到其他 location 或虚拟服务器。 此外,还可以返回特定的错误代码,可以为每个错误代码配置特定的页面。
1. 设置虚拟服务器 server
Nginx 配置文件中至少要包含一个 server 指令定义的虚拟服务器。在处理每个请求之前,需要先选中一个 server。
在 http 上下文中通过 server 来定义虚拟服务器,可以定义多个:
http {
server {
# Server1 configuration
}
server {
# Server2 configuration
}
}
在 server 的配置块中,通常包含 listen 指令指定要监听的 IP 地址和端口号 (or Unix domain socket and path)。IPv4 和 IPv6 地址都可以。
server {
listen 127.0.0.1:8080; # 监听本机发出的,连到 8080 端口的请求
# The rest of server configuration
}
参数省略端口号时,使用标准 80 端口。省略地址时,监听所有地址。如果整个 listen 指令都省略了,根据超级用户的权限不同,标准端口是 80/tcp,默认端口是 8000/tcp。
如果有几个虚拟服务器同时匹配到请求的 IP 地址和端口号,Nginx 会用 HTTP 头中的域名(Host 头)匹配服务器配置块中的 server_name
指令。server_name
指令的参数可以是完整域名,通配符(*
)或正则表达式。
server {
listen 80;
server_name example.org www.example.org;
...
}
如果匹配 Host 头的服务器名有多个,Nginx 会按以下顺序搜索并使用第一个匹配到的服务器:
- 精确名称搜索,比如 www.baidu.com。
- 以通配符 * 开头的最长匹配项,比如 *.baidu.com。
- 以通配符 * 结尾的最长匹配项,比如 mail.*。
- 第一个匹配到的正则表达式。
如果 Host 头字段匹配不到任何服务器,Nginx 会把请求路由到默认服务器。默认服务器可以通过 listen
指令后的 default_server
参数来声明,如果没有声明则配置文件中的第一个服务器就成为默认服务器。
server {
listen 80 default_server;
...
}
2. 设置位置 locations
Nginx 可以将流量发送到不同的代理服务器,或者根据请求 URI 提供不同的文件。通过在 server 指令中使用 location 指令实现这些功能。
例如,可以在虚拟服务器中定义 3 个 location 块,将一部分请求发到被代理的服务器,另一部分请求发到另一个被代理的服务器,其他请求则映射到本地的文件系统。
Nginx 会根据 location 指令的参数来测试请求的 URI,并使用匹配到的 location 指令。location 块通常可以嵌套,实现对请求的分组处理。
location 指令有两种参数:前缀字符串(路径名)和正则表达式。要匹配前缀字符串的请求 URI 必须以这个前缀字符串开头才会匹配成功。
2.1 前缀字符串(路径名)
下面的 location 指令参数使用了路径名,请求 URI 要匹配这个 location,就必须以 /some/path/开头,比如 /some/path/document.html。而 /my-site/some/path 就不匹配。
location /some/path/ {
...
}
2.2 正则表达式
- 以
~
开头时,表示区分大小写; - 以
~*
开头时,表示不区分大小写。
下面的例子中,如果请求 URI 中的任何位置出现 .html 或 .htm 就匹配成功:
location ~ \.html? {
...
}
2.3 location 匹配原则
为了找出最匹配 URI 的 location,Nginx 会先比较前缀字符串(路径名),再比较正则表达式。
正则表达式的优先级较高,除非使用 ^〜 修饰符。在前缀字符串中,Nginx 选择最特定的字符串(即最长和最完整的字符串)。 下面给出了选择处理请求的位置的确切逻辑:
- 用所有的前缀字符串测试 URI。
- 等号
=
定义了前缀字符串和 URI 的精确匹配关系。如果找到了这个精确匹配,则停止查找。 - 如果
^~
修饰符预先匹配到最长的前缀字符串,则不检查正则表达式。 - 存储最长的匹配前缀字符串。
- 用正则表达式测试 URI。
- 匹配到第一个正则表达式后停止查找,使用对应的 location。
- 如果没有匹配到正则表达式,则使用之前存储的前缀字符串对应的 location。
等号 =
最常见的用途就是 /
(forward slash)。如果对 / 的请求很多,可以把 = /
指定为 location 的参数来提高响应速度。因为在第一次比较之后就停止搜索匹配。
location = / {
...
}
2.4 root 指令和 proxy_pass 指令
location 上下文中可以定义处理请求(提供静态文件或转发请求给被代理的服务器)的指令。
server {
# 响应静态文件
location /images/ {
root /data;
}
# 转发请求到 http://www.example.com
location / {
proxy_pass http://www.example.com;
}
}
2.4.1 root 指令
root 指令指定静态文件在文件系统中的路径。请求的 URI 中,和 location 相关的部分会和 root 指定的路径组合成完整的目录名和文件名,从而获取到静态文件。例如,上面的例子中对 /images/example.png
的请求,会对应文件 /data/images/example.png
。
2.4.2 proxy_pass 指令
proxy_pass 指令将请求转发给和 URL 相关联的被代理的服务器,然后把被代理的服务器的响应转发给客户端。上面的例子中,所有 URI 不以 /images/ 开头的请求,都会被转发给被代理的服务器。
3. 使用变量
可以通过在配置文件中使用变量,实现不同的情况下用不同的方式处理请求。变量是在运行时计算的有名字的值,用作指令的参数。Nginx 中的变量用美元符号 $
开头。变量根据 Nginx 的状态定义信息,例如当前正在处理的请求的属性。
Nginx 中有一系列的预定义的变量,比如 core HTTP 变量。还可以通过 set、map 和 geo 指令来自定义变量。多数变量在运行时才计算值,并包含针对特定请求的信息。例如,$remote_addr
包含客户端的 IP 地址信息,$uri
包含当前的 URI 值。
4. return 指令,返回指定的状态码(Status Codes)
有的网页的 URI 需要立刻返回包含指定错误码或跳转码的响应,比如页面被暂时或永久移动。实现这个目的的最简单方法是使用 return 指令。
location /wrong/url {
return 404;
}
return 指令的第一个参数是响应码。第二个参数可选,可以是重定向的 URL(用于响应码 301,302,303,307)或响应体中的文本。
location /permanently/moved/url {
return 301 http://www.example.com/moved/here;
}
location 和 server 上下文中都可以使用 return 指令。
5. 重写请求的 URI
通过 rewrite 指令,在请求的处理过程中可以多次改写请求的 URI。
rewrite 指令有3个参数(两个必填,一个选填):第一个参数是用于匹配 URI 的正则表达式,第二个参数是用于替代匹配到的 URI 的新的 URI,第三个参数可选,是一个标志,可以停止处理其他重写指令或发送重定向(代码301或302)。
location /users/ {
rewrite ^/users/(.*)$ /show?user=$1 break;
}
在 server 和 location 上下文中可以使用多个 rewrite 指令,Nginx 会按照先后顺序依次执行。在 server 上下文中的 rewrite 指令会在上下文被选中时执行一次。
Nginx 在处理完一系列的 rewrite 指令后,会根据新的 URI 选择一个 location 上下文。如果选中的 location 包含 rewrite 指令,会依次执行。
rewrite 指令示例:
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。因为第三个参数使用了 last 标志,后续指令(第二个重写和返回指令)被跳过,但 Nginx 继续处理请求(该请求现在已经具有不同的 URI)。如果 URI 不匹配 rewrite 指令,Nginx 会向客户端返回 403 错误代码。
有两个参数可以中断 rewrite 指令:
- last – 在当前的 server 或 location 上下文中中断 rewrite 指令的执行。此时 Nginx 会查找要重写 URI 的 location,并且应用新 location 中的任何 rewrite 指令(意味着 URI 可以再次更改)。
- break – 跟 break 指令类似,在当前的上下文中停止 rewrite 指令,停止查找匹配新的 URI 的 location。新的 location 中的 rewrite 指令不会执行。
6. 重写 HTTP 响应(Rewriting HTTP Responses)
sub_filter
指令可以定义如何重写或改变 HTTP 响应的内容,将一个字符串替换为另一个字符串。。这个指令支持变量和链式改变,从而可以实现复杂操作。
示例:
location / {
sub_filter /blog/ /blog-staging/;
sub_filter_once off;
}
可以改变 HTTP 协议,将 http://
改为 https://
,也可以把请求头字段中的 localhost 改为主机名。sub_filter_once
指令控制 Nginx 是否在一个 location 内连续应用 sub_filter
指令。
location / {
sub_filter 'href="http://127.0.0.1:8080/' '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
修改的响应部分再次被另一个 sub_filter
匹配,是不会再次被替换的。
7. 处理错误(Handling Errors)
error_page
指令可以配置 Nginx 返回自定义页面以及错误代码,在响应中替换不同的错误代码,或将浏览器重定向到指定 URI。
以下示例中,error_page
指令指定要返回 404 错误页面(/404.html)。
error_page 404 /404.html;
注意 error_page
指令并不会立刻返回错误(只有 return 指令才会立刻返回),只是表明如何发生错误时如何处理。错误代码可以来自一个被代理的服务器或在 Nginx 处理过程中发生(比如 Nginx 找不到客户端指定的页面时会 404)。
下面例子中,当 Nginx 找不到页面,会把 404 代码改为 301,并把客户端重定向到 http:/example.com/new/path.html
。
location /old/path.html {
error_page 404 =301 http:/example.com/new/path.html;
}
下面的配置会在文件找不到时把请求转发到后端。由于在 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 在文件找不到时做内部跳转。error_page
指令的最后一个参数中可以使用 $uri
变量,代表当前的请求中的 URI,该请求将在重定向中传递。
例如,如果文件 /images/some/file 找不到,会被替换为 /fetch/images/some/file 并从第一个 location 开始再次查找。最终,匹配第二个 location 上下文并跳转到代理服务器。 open_file_cache_errors
指令可防止在找不到文件时写入错误消息。这里因为找不到文件时立刻处理了,所以没有必要使用。