nginx web服务器

  1. Setting Up Virtual Servers
  2. Configuring Locations
  3. Using Variables
  4. Returning Specific Status Codes
  5. Rewriting URIs in Requests
  6. Rewriting HTTP Responses
  7. Handling Errors

官方文档[](https://www.nginx.com/resources/admin-guide/nginx-web-server/)学习

在Nginx中每个用来处理HTTP请求的virtual server都被称为location. location可以参与请求处理的整个过程. location可以代理一个请求或者直接返回一个文件. 不但如此, 在location中我们还可以修改URI访问路径, 这样我们就可以将该请求指向其他的location或者其他的virtual server.

Setting Up Virtual Servers

Nginx插件配置必须最少包含一个server指令(用于定义虚拟服务器-virtual server). 当Nginx插件处理一个请求时, 它首先会选择处理该请求的虚拟服务器. 一个虚拟服务器定义在http上下文里, 例如:

1
2
3
4
5
http {
server {
# Server configuration
}
}

http上下文里可以定义多个server指令, 这样一来就可以实现多个虚拟服务器了.

server指令里通常包含一个listen指令, 通过listen指令来指定监听请求的IP地址和端口号(或者Unix domain socket and path). IPv4 和 IPv6 地址都可进行配置

下面的例子演示了在IP地址127.0.0.1和端口8080上进行网络事件监听

1
2
3
4
server {
listen 127.0.0.1:8080;
# The rest of server configuration
}

如果端口忽略不写的话, Nginx插件会使用标准端口. 还有如果IP地址忽略不填的话, Nginx插件会在所有的IP地址上进行网络事件监听. 如果没有配置listen指令的话, 在超级用户权限下, 标准端口是80/tcp, 默认端口8000/tcp.

如果有多个server指令里配置的IP地址和端口匹配到请求, 那么Nginx插件会根据请求的Host header字段与server_name指令进行匹配. 我们可以将server_name指令配置成一个全名称的地址, 或者使用通配符, 正则表达式. 如果想要使用通配符的话, 可以在地址的开头或者结尾使用*.

1
2
3
4
5
server {
listen 80;
server_name example.org www.example.org;
...
}

如果有多个server_name指令匹配到Host header, Nginx插件会根据如下顺序进行匹配查找, 直到找到第一个.

  1. 名字完全符合
  2. *开始的, 最符合的名字, 例如*.example.org
  3. *结束的, 最符合的名字, 例如mail.*
  4. 第一个匹配正则表达式的

如果请求中的Host header没有匹配到一个合适的server name, Nginx插件会将该消息路由到默认服上面去. 默认服是在nginx.conf文件里进行配置的. 我们也可以使用default_server参数在listen指令中进行显示设定.

1
2
3
4
server {
listen 80 default_server;
...
}

Configuring Locations

Nginx插件可以根据请求的URI将请求派发到不同的代理上, 或者处理不同的文件. 这种功能是通过server指令里的location指令完成的.

例如你可以定义三个location指令, 一个用来将请求派发到一个代理服务器, 一个用来将其他的请求派发到另外的一个代理服务器,最后的一个用来提供本地文件系统服务.

NGINX Plus tests request URIs against the parameters of all location directives and applies the directives defined in the matching location. Inside each location block, it is usually possible (with a few exceptions) to place even more location directives to further refine the processing for specific groups of requests.

注意, 在这篇教程中, location这个字指的是一个单独的location上下文.

有俩种location指令参数

  • prefix strings : 请求的URI是以固定的字符串开头, 例如prefix strings是/some/path/, 那么/some/path/document.html就是这种类型的, 但是/my-site/some/path就不是.
  • regular expressions :
1
2
3
location /some/path/ {
...
}

当使用表达式的时候, 如果使用~开头则表示要区分大小写. 如果以~*开头,则表示忽略大小写. 下面的例子是只要请求包含.html或者.htm这些字符串就都匹配

1
2
3
location ~ \.html? {
...
}

Nginx插件进行location匹配时, 优先进行prefix string匹配, 如果prefix string匹配不到再进行正则匹配.

Higher priority is given to regular expressions, unless the ^~ modifier is used. Among the prefix strings NGINX Plus selects the most specific one (that is, the longest and most complete string). The exact logic for selecting a location to process a request is given below:

  1. Test the URI against all prefix strings.
  2. The = (equals sign) modifier defines an exact match of the URI and a prefix string. If the exact match is found, the search stops.
  3. If the ^~ (caret-tilde) modifier prepends the longest matching prefix string, the regular expressions are not checked.
  4. Store the longest matching prefix string.
  5. Test the URI against regular expressions.
  6. Break on the first matching regular expression and use the corresponding location.
  7. If no regular expression matches, use the location corresponding to the stored prefix string.

=的典型用例是请求/, 如果请求/是非常频繁的, 在location指令上设置= /可以大幅提升访问速度, 这是因为在路径搜索匹配时, 一旦匹配到就不再继续搜素匹配了, 节约了时间.

1
2
3
location = / {
...
}

location上下文中可以包含多个指令, 用于说明如何处理一个请求, 例如是想提供静态文件服务还是想向一个代理服务器派发请求. 例如, 在下面的例子中, 第一个请求就是提供/data目录下的静态文件服务, 第二个请求就是向一个代理服务器www.example.com派发请求.

1
2
3
4
5
6
7
8
9
10
server {
location /images/ {
root /data;
}

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

  • root指令用于指定提供静态文件服务的文件系统路径. 如果有一个请求/images/example.png, 那么nginx插件会在文件系统中进行如下搜索/data/images/example.png.
  • proxy_pass指令会将请求派发到代理服务器上. 在上面的例子中, 只要是请求不是以/images/开头的, 那么请求都会派发到那个代理服务器上.

Using Variables

你可以在配置文件里使用变量, 以便Nginx插件可以根据不同的情况进行不同的处理. 变量在运行时进行计算, 然后将值传递给指令使用. 我们可以通过$来引用一个变量(例如$time).

在Nginx里已经为我们提前定义好了一些变量, 例如core HTTP. 而且你也可以使用set, map, geo等指令自定义一些变量.

Returning Specific Status Codes

在一些web站点中, 由于资源移除或者其他原因, 我们需要直接给前端返回一个错误码, 那么我们可以使用return指令来完成.

1
2
3
location /wrong/url {
return 404;
}

下面的例子中, 第一个参数是一个错误码. 第二个参数是一个可选参数(代表一个重连接的URL)

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

return指令既可以放在location指令里, 也可以放在server指令里

Rewriting URIs in Requests

请求URI可以在被处理时通过rewrite指令被多次修改, rewrite指令包含一个可选参数和俩个必选参数.

  • 第一个参数(必填)是用来匹配请求的正则表达式.
  • 第二个参数(必填)是用来指向重连接所需要匹配的URI.(下面的例子中我们采用的是正则表达式进行匹配)
  • 第三个参数(选填)是用来标记是继续执行下一个重连接还是执行其他操作(例如返回一个错误码)
    1
    2
    3
    location /users/ {
    rewrite ^/users/(.*)$ /show?user=$1 break;
    }

server或者location指令中可以包含多个rewrite指令. Nginx插件会根据rewrite指令出现的顺序依次执行.

After NGINX processes a set of rewriting instructions, it selects a location context according to the new URI. If the selected location contains rewrite directives, they are executed in turn. If the URI matches any of those, a search for the new location starts after all defined rewrite directives are processed.

下面的例子演示里rewritereturn指令混合使用的方式

1
2
3
4
5
6
7
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标记, 因此下面的rewritereturn指令就不会再执行到了. 但是如果URI例如/download/some/audio/file并不符合第一个,那么Nginx插件会继续匹配到第二个, 当然第二个符合, 于是URI被替换成/download/some/mp3/file.ra. 如果前俩个都不符合的话, 那么就会返回403错误码.

下面, 我们会介绍俩种终止URIrewrite执行的方式:

  • last – 这种方式只是会在当前的server或者location上下文中停止, 但是Nginx插件会对重写的URI继续在location里进行查找.
  • break – 它不会对新的URI在当前上下文中进行查找, 而是简单的终止掉整个过程.

Rewriting HTTP Responses

有时候你可能需要重写或者替换HTTP响应中的内容, 或者将一个字符串替换为另一个. sub_filter指令可以实现类似于这种的需求. sub_filter支持变量以及链式替换, 以便实现更加复杂的重写功能.

1
2
3
4
location / {
sub_filter /blog/ /blog-staging/;
sub_filter_once off;
}

另一个例子是将http://改变为https://, 而且将localhost地址改变为请求头中的主机名. sub_filter_once指令是告诉NGINX在location指令里可以开启多个sub_filter.

1
2
3
4
5
location / {
sub_filter 'href="http://127.0.0.1:8080/' 'href="http://$host/';
sub_filter 'img src="http://127.0.0.1:8080/' 'img src="http://$host/';
sub_filter_once on;
}

注意, 如果响应已经在一个sub_filter修改过里, 那么即使另一个sub_filter也匹配到了,但是不会再次修改.

Handling Errors

使用error_page指令, 我们可以配置Nginx插件返回一个自定义的界面或者响应一个不同的错误码,甚至可以重定向到一个新的地址上面去. 在下面的例子中, error_page指令会在发生错误时返回一个指定的界面/404.html, 而不是像默认的返回一个404错误码.

1
error_page 404 /404.html;

注意, 这个指令并不是直接就返回了(return指令直接返回), 而是指定一种当错误发生时的一种处理方式.

在下面的例子中, 当Nginx找不到文件时, 它会返回301错误码, 同时告诉客户端重定向到http:/example.com/new/path.html这个界面上.

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

The following configuration is an example of passing a request to the back end when a file is not found. Because there is no status code specified after the equals sign in the error_page directive, the response to the client has the status code returned by the proxied server (not necessarily 404).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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/;
}
}

The error_page directive instructs NGINX Plus to make an internal redirect when a file is not found. The $uri variable in the final parameter to the error_page directive holds the URI of the current request, which gets passed in the redirect.

For example, if /images/some/file is not found, it is replaced with /fetch/images/some/file and a new search for a location starts. As a result, the request ends up in the second location context and is proxied to http://backend/.

The open_file_cache_errors directive prevents writing an error message if a file is not found. This is not necessary here since missing files are correctly handled.