重点关注
1. Location 的匹配顺序
Nginx 使用正常化 (normalized) 后的 request_uri 和配置文件中的 location{} 进行匹配。如果匹配成功,按照匹配到的 location 中的配置处理该请求。如果匹配失 败,Nginx 默认返回 404 或 403 错误页面。
- location 修饰符和分类:
- None - 字面量前缀匹配
- = - 字面量精确匹配
- ~ - 正则匹配
- ~* - 大小写不敏感正则匹配
- ^~ - 略过正则匹配
- @ - 内部跳转匹配 (named location)
- location 匹配顺序:
- 在 字面量前缀匹配 和 字面量精确匹配 类型的 location 中选择最长匹 配/相等的那个 location (查找顺序与配置文件中 location 的顺序无关);
- 如果第 1 步查找到的 location 是 按字面量精确匹配 类型,Nginx 使用该 location 处理当前请求;
- 如果第 1 步查找到的 location 是 略过正则匹配 类型,Nginx 使用该 location 处理当前请求;
- 如果配置文件中没有配置任何 正则 location ,Nginx 使用第 1 步匹配到的 location 处理当前请求;
- 如果配置文件中存在 正则 location ,按其在配置文件中出现的顺序使用 正则规则进行匹配,Nginx 使用最先命中的 正则 location 处理当前请求;
- 如果 正则 location 和当前请求都不匹配,Nginx 使用第 1 步匹配到的 location 处理当前请求;
- 另外:
- 正则 location 中可以使用正则的 capture 语法,并且 captured 到的 数据可以在当前 location 中通过 N 的方式使用;
- 使用 @ 的 内部跳转匹配 不直接用于请求处理。它们可以作为其它 location 中 重定向 的目标使用;
- location 可以嵌套定义,但是:
- = 字面量精确匹配 location 中不能嵌套其它 location ;
- @ 内部跳转区配 location 中不能嵌套,也不能嵌套到其它 location 中;
- 默认 Location - server {} 中未定义任何 location 或者定义的 location 都未能和请求 URL 匹配时,Nginx 会使用默认 location 尝试处理该请求。默认处理 方式由 NGX_HTTP_CONTENT_PHASE 注册的模块确定,一般依次为查找并返回静态文 件、返回目录文件列表、尝试查找 index.html 等。如果默认处理失败,Nginx 返 回 404 或 403 错误页面。
2. 配置项的继承
Nginx 的配置文件使用了自定义语法,使用 {} 块表示配置项的作用域(全局作用 域除外),也即上下文(Context)。 同时,作用域可以进行嵌套,并且,一般情况下, 内层作用域会从外层作用域继承配置项,这样就增加了配置文件的灵活度。
常见的作用域有: 全局作用域, http {}, server {}, location {}, if {}, map {}, mail {}, stream {}, upstream {} 等。
作用域可以嵌套,有些必须嵌套到其它作用域。比如, server {} 只能出现在 http {}, mail {} 或者 stream 中; location {} 只能出现在 server {} 中等等。同时,Nginx 也规定了配置项允许使用的作用域。比如, 官方文档 中对 root 配置项的描述如下:
Syntax: root path; Default: root html; Context: http, server, location, if in location
其中 Context 字段就用于描述 root 配置项可以在哪些作用域中使用。 同时, Nginx 配置文件中的内部作用域可以从它的上层作用域继承某些配置 (The default inheritance mode is that directives inherit downwards only),增加 了配置文件书写的灵活性和配置文件的表达能力。
下面,我们将配置项按行为进行分类,并详细说明它们在嵌套作用域中是如何继承的:
一般配置项 (Normal Directive)
- 在其所在的 Context 中只能使用一次;
- 遵守默认继承规则:外层作用域的配置项会被嵌套内层继承;
- 举例: root, index 等配置项;
数组类配置项 (Array Directive)
- 这类配置项可以在一个 Context 中出现多次,每次配置的参数组装成 "数组" 的 形式由 Nginx 识别使用;
- 遵守默认继承规则:外层作用域的配置项会被嵌套内层继承;
- 举例: fastapi_param 等配置项;
动作类配置项 (Action Directive)
- 这类配置项只在当前作用域生效,不会被继承;
- 官方文档对这类配置项的行为没有准确描述,并且具体行为依赖模块实现;
- 举例: set, rewrite, proxy_pass 等配置项;
Rewrite 模块的继承示例
server { set $a a; location /set/ { set $b b; location /set/c/ { set $c c; } } }
try_files
- try_files 配置项和 动作类配置项 类似,但它有个特殊的地方:当它出现在 server {} 块时,它创建了一个 伪 location ,如果请求和其它 location 匹配上时,该 伪 location 会被完忽略。所以,尽量只将它用在 location {} 块中。
3. Nginx 何时使用 chunked 编码
chunked 编码是什么?
- HTTP 协议中传输编码和内容编码的区别和作用;
- HTTP 使用「内容编码」对原始数据(文本、二进制、图像、视频等)进行编码,比如
,可以使之更安全或者更省传输流量。相关包头有: Content-Type,
Content-Encoding, Content-Length, Content-Language 。
- Content-Encoding 的可选值有: gzip (表明实体采用 GUN zip 编码)、 compress (表明实体采用 Unix 的文件压缩程序)、 deflate (表明实体 是采用 zlib 格式压缩的), identify (默认值,表示未对实体进行任何编码 )。
- HTTP 使用的「传输编码」和「内容编码」的区别是,它对整个报文实体进行可逆变
换,但是使用它们是由于架构方面的原因,同内容的格式无关。HTTP 协议中只定义了
下面两个包头用于描述和控制传输编码:
- Transfer-Encoding - HTTP 1.1 响应包头,表示对报文进行了何种编码。常见 值有 chunked 。 chunked 是 HTTP 1.1 兼容程序必须支持的传输编码格 式。
- TE - HTTP 1.1 请求包头,表示服务器可以使用的传输编码的范围。常见值有 trailer 和 chunked 。
- HTTP 使用「内容编码」对原始数据(文本、二进制、图像、视频等)进行编码,比如
,可以使之更安全或者更省传输流量。相关包头有: Content-Type,
Content-Encoding, Content-Length, Content-Language 。
- 为什么要使用 chunked 格式的传输编码?
- 如果内容主体不是事先生成的,在传输过程中经过的网关和内容编码器无法事先确定 报文主体的大小。通常,服务器为了性能和效率,希望在确定内容大小之前就可以开 始传输数据。
- 有时场合里,服务器需要在传输前对数据进行安全相关的处理。
- 在持久连接上传输内容主体,必须使用 Content-Length 包头或者 chunked 传输编码。
如何完整接收 HTTP 响应数据?
- Content-Length 包头;
- Transfer-Encoding 包头;
- 如果同时出现了 Content-Length 和 Transfer-Encoding 时,需要忽略 Content-Length 包头;
- 如果报文使用了 multipart 媒体类型,并且没有使用 Content-Length 指明实 体主体的长度,那么 multipart 中的每个部分都必须说明自己的大小;
- 在使用较老的 HTTP 协议版本 (HTTP 1.0-)里,或者和实现不正确的 Web 服务器交 互过程中,如果响应包未通过 Content-Length 和 Transfer-Encoding: chunked 指明报文主体结束边界时,可以把服务端主动关闭连 接作为报文完整接收的标志;
常见模块和 chunked
Nginx uses chunked if (a) response length isn't known (i.e. backend doesn't return it) or (b) it has to modify response body.
The (b) includes gzip filter, ssi filter, addition filter, and charset filter. With all of them swiched off (default) you should see original Content-Length from backend's response (if any).
4. 如何隐藏 Nginx 版本信息
Syntax: server_tokens on | off | build | string; Default: server_tokens on; Context: http, server, location Enables or disables emitting nginx version on error pages and in the "Server" response header field.
5. server_name 和 Host
server_name 为 Nginx 虚拟主机指定“名字”,随后根据请求 Host 包头中的值与 该“名字”进行对比,以便为请求选择合适的虚拟主机。
- 相关文档
- server_name 指定的名字可以用以下几种格式表示:
- 字面常量。如,www.example.com 。其中 "" 可以用于匹配没有 Host 包头的请求。
- 通配符。 * 只能出现在名字开始或结尾,并和 . 号相邻。如, *.example.com, www.example.* 。其中, .example.com 这类特殊写法 表示该名字即可以用于匹配 example.com ,又可以用于匹配 *.example.com 。
- 正则表达式。此时名字必须以 ~ 开头。 如, ~^(?<user>.+)\.exmample\.net$
- Host 和配置中所有虚拟主机的名字按如下顺序尝试匹配,匹配过程在第一个命中处
结束:
- exact name
- longest wildcard name starting with an asterisck, e.g. *.example.com
- longeet wildcard name ending with an asterisk, e.g. mail.example.*
- first matching regular expression (in order of appearance in a configuration file.)
6. try_files 配置项
Syntax: try_files file ... uri; try_files file ... =code; Default: --- Context: server, location Check the existence of files in the specified order and uses the first found file for request processing; the processing is performed in the current context. The path to a file is constructed from the *file* parameter according to the **root** and **alias** directives. It is possible to check directory's existence by specifying a slash at the end of a name, e.g. "$uri/". If none of the files were found, an internal redirect to the *uri* specified in the last parameter is made.
几个需要特别注意的点:
- 外层作用域里的 try_files 不会被内层继承;
- 定义在 server {} 中的 try_files 配置项创建 伪 location , server {} 中定义的其它 location (不包括 默认 location )未匹配上请求 时,Nginx 使用 try_files 的 伪 location 处理该请求;
用法举例
替代 if 过滤掉有问题的请求
location ~* \.php $ { try_files $uri =404; fastcgi_pass backend; ... }
在请求转交 Upstream 之前,先尝试读取静态文件
location / { try_files $uri $uri/ @proxy; } location @proxy { include fastcgi.conf; fastcgi_pass unix:/tmp/php-fpm.sock; }
7. rewrite 配置项
rewrite 配置项由 rewrite 模块提供,该模块使用 PCRE 正则表达式规则,可 以对请求 URI 进行调整、重定向请求或者根据条件选择用于处理请求的配置。
rewrite 模块提供的配置项会在Nginx 请求处理几大阶段中的 SERVER_REWRITE 阶段(定义于 server {} 作用域)和 REWRITE 阶段(定义于 location {} 或 者 if {} 作用域)生效。并且,该模块提供的配置项是「动作配置项」,它们出在在 嵌套作用域里时,不会被继承。
本小节主要介绍 rewrite 配置项的相关用法。
Syntax: rewrite regex replacement [flag]; Default: --- Context: server, location, if If the specified regular expression matches a request URI, URI is changed as specified in the *replacement* string. The rewrite directives are executed sequentially in order of their appearance in the configuration file. It is possible to terminiate further processing of the directives using flags. If a replacement string starts with "http://", "https://", or "$scheme", the processing stops and the redirect is returned to a client. An optional *flag* parameter can be one of: last stops processing the current set of ngx_http_rewrite_module directives and starts a search for a new location matching the changed URI; break stops processing the current set of ngx_http_rewrite_module directives as with the ``break`` directive; redirect returns a temporary redirect with the 302 code; used if a replacement string does not start with "http://", "https://", or "$scheme"; permanent returns a permanent redirect with the 301 code.
几个需要注意的点:
定义于 server {} 作用域的 rewrite 配置项在 SERVER_REWRITE 阶段生 效。也就是说,Nginx 在确定和请求匹配的 location 前( FIND_CONFIG 阶 段),Nginx 就会使用 rewrite 指定的配置对请求的 URI 进行处理;
尽量避免使用 rewrite 以节省正则表达式的处理开销,例如:
rewrite ^/(.*)$ http://domain.com/$1 permanent; # bad return 301 http://domain.com$request_uri; # good
如果 replacement 是以 http://, https:// 或者 $scheme 开始的字符 串时(此时 flag 会忽略), rewrite 模块执行结束,并向客户端返回 302 重定 向;
rewrite 如果出现在 正则 location 中并且该 location 使用了 capture 语法时,被 captured 的内容不能用作 rewrite 配置项的参数;
如果 rewrite 没有指定任何 flag ,并且 regex 和请求 URI 匹配时,
如果后面没有其它 rewrite 配置项,Nginx 会将此配置项作用于请求 URI 后, 再为请求重新匹配 location (行为类似 last);
如果后面有其它 rewrite 配置项,并且这些配置项都未指定 flags 时,Nginx 会将这些 rewrite 配置项作用于请求 URI 后,再为请求重新匹配 location(行 为类似 last );
location /lua { rewrite ^/(.*) /a/$1; rewrite ^/(.*) /b/$1; echo "same location: $uri"; } location / { echo "root location: $uri"; }
如果后面有其它 rewrite 配置项,并且可以对该请求 URI 生效的配置项有具体 flag 参数,那么随后的行为受该 flag 影响;
location /lua { rewrite ^/(.*) /a/$1; rewrite ^/(.*) /b/$1 break; echo "same location: $uri"; } location / { echo "root location: $uri"; }
8. 「邪恶的」 if
if 是 ngx_http_rewrite_module 提供的一个配置项。它为 Nginx 配置文件提供了根据条件是否成立来决 定使用哪些配置项的能力。
if 配置的使用相对简单,但是因为它强大的功能和 Nginx 配置文件模型的各种隐式规则,在不明就理的情 况下,可能会得到「出乎意料」的结果,比如 Agentzh 写的这篇 If is evil 就总结了很多 if 配置上的坑。 文章建议:
It's generally a good idea to avoid it if possible.
这篇文章 从源代码的角度解释了上文总结的 if 配置示例。从中可以看到,有些 if 相关的问题已经在新版 Nginx 中解决了,但是有些问题依旧存 在。
9. 配置本地缓存
proxy_cache_path /dev/shm/ngx/baseapi levels=1:2 keys_zone=baseapi:400m inactive=2h max_size=1g; ... server { ... location / { ... proxy_cache baseapi; proxy_cache_use_stale error; proxy_cache_use_stale timeout; proxy_cache_use_stale updating; proxy_cache_lock on; proxy_cache_lock_age 5s; proxy_cache_lock_timeout 5s; proxy_cache_background_update on; proxy_cache_valid 200 301 302 4m; proxy_pass http://127.0.0.1:8012; } }
10. HTTPS 配置
server { listen 443 ssl; ... # `openssl ciphers` to show full list of ciphers openssl supports # See: https://mozilla.github.io/server-side-tls/ssl-config-generator/ # See: https://www.ssllabs.com/ssltest/ ssl_ciphers 'XXXXX-XXXXX"; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:50m;ssl_session_timeout 1h; ssl_verify_client off; ssl_dhparam certificates/dhparam.pem; ssl_certificate certificates/general/server.pem; ssl_certificate_key certificates/general/server.key; ... }
Comments
不要轻轻地离开我,请留下点什么...