理清 nginx 中的 location 配置
原文:一文理清 nginx 中的 location 配置(系列一)
参考:Nginx location 能否匹配到问号里的参数?
另:可在 nginx.viraptor.info 上测试 location 匹配
前言
location 指令是 nginx 中最关键的指令之一,location 指令的功能是用来匹配不同的 URI 请求,进而对请求做不同的处理和响应,这其中较难理解的是多个 location 的匹配顺序,本文会作为重点来解释和说明。
开始之前先明确一些约定,我们输入的网址叫做请求 URI,nginx 用请求 URI 与 location 中配置的 URI 做匹配。
nginx 文件结构
首先我们先简单了解 nginx 的文件结构,nginx 的 HTTP 配置主要包括三个区块,结构如下:
Global: nginx 运行相关 Events: 与用户的网络连接相关 http http Global: 代理,缓存,日志,以及第三方模块的配置 server server Global: 虚拟主机相关 location: 地址定向,数据缓存,应答控制,以及第三方模块的配置
从上面展示的 nginx 结构中可以看出 location 属于请求级别配置,这也是我们最常用的配置。
配置 location 块
location 语法
location 块通过指定模式来与客户端请求的 URI 相匹配。
location 基本语法:
- 匹配 URI 类型,有四种参数可选,当然也可以不带参数。
- 命名 location,用 @ 来标识,类似于定义 goto 语句块。
location [ = | ~ | ~* | ^~ ] /URI { … } location @/name/ { … }
location 匹配命令解释
参数 | 解释 |
---|---|
空 |
location 后没有参数直接跟着标准 URI,表示前缀匹配,代表跟请求中的 URI 从头开始匹配。 |
= |
用于标准 URI 前,要求请求字符串与其精准匹配,成功则立即处理,nginx 停止搜索其他匹配。 |
^~ |
用于标准 URI 前,并要求一旦匹配到就会立即处理,不再去匹配其他的那些个正则 URI,一般用来匹配目录 |
~ |
用于正则 URI 前,表示 URI 包含正则表达式,区分大小写 |
~* |
用于正则 URI前,表示 URI 包含正则表达式,不区分大小写 |
@ |
@ 定义一个命名的 location,@ 定义的 location 名字一般用在内部定向,例如 error_page, try_files 命令中。它的功能类似于编程中的 goto。 |
location 匹配顺序
nginx 有两层指令来匹配请求 URI。第一个层次是 server 指令,它通过域名、ip 和端口来做第一层级匹配,当找到匹配的 server 后就进入此 server 的 location 匹配。
location 的匹配并不完全按照其在配置文件中出现的顺序来匹配,请求 URI 会按如下规则进行匹配:
- 先精准匹配
=
,精准匹配成功则会立即停止其他类型匹配; - 没有精准匹配成功时,进行前缀匹配。先查找带有
^~
的前缀匹配,带有^~
的前缀匹配成功则立即停止其他类型匹配,普通前缀匹配(不带参数^~
)成功则会暂存,继续查找正则匹配; =
和^~
均未匹配成功前提下,查找正则匹配~
和~*
。当同时有多个正则匹配时,按其在配置文件中出现的先后顺序优先匹配,命中则立即停止其他类型匹配;- 所有正则匹配均未成功时,返回步骤 2 中暂存的普通前缀匹配(不带参数
^~
)结果。
以上规则简单总结就是优先级从高到低依次为(序号越小优先级越高):
1. location = # 精准匹配 2. location ^~ # 带参前缀匹配 3. location ~ # 正则匹配(区分大小写) 4. location ~* # 正则匹配(不区分大小写) 5. location /a # 普通前缀匹配,优先级低于带参数前缀匹配,多个前缀匹配命中时,取较长的那个 6. location / # 任何没有匹配成功的,都会匹配进这里处理
上述匹配规则可以用以下伪代码表示,加深理解:
function match(uri): rv = NULL if uri in exact_match: return exact_match[uri] if uri in prefix_match: if param of prefix_match[uri] is '^~': return prefix_match[uri] else: rv = prefix_match[uri] // 注意这里没有 return,且这里是最长匹配 if uri in regex_match: return regex_match[uri] // 按文件中顺序,找到即返回 return rv
案例分析
接下来,让我们通过一些实际案例来验证上述规则。
案例 1
server { server_name website.com; location /doc { return 701; # 用这样的方式,可以方便知道请求到了哪里 } location ~* ^/document$ { return 702; } } curl -I website.com:8080/document 返回 HTTP/1.1 702
说明:按照上述的规则,显然第二个正则匹配会有更高的优先级
案例 2
server { server_name website.com; location /document { return 701; } location ~* ^/document$ { return 702; } } curl -I website.com:8080/document 返回 HTTP/1.1 702
说明:第二个匹配了正则表达式,优先级高于第一个普通前缀匹配
案例 3
server { server_name website.com; location ^~ /doc { return 701; } location ~* ^/document$ { return 702; } } curl http://website.com/document 返回 HTTP/1.1 701
说明:第一个前缀匹配^~
命中以后不会再搜寻正则匹配,所以会第一个命中
案例 4
server { server_name website.com; location /docu { return 701; } location /doc { return 702; } } curl -I website.com:8080/document 会返回 HTTP/1.1 701 server { server_name website.com; location /doc { return 702; } location /docu { return 701; } } curl -I website.com:8080/document 依然返回 HTTP/1.1 701
说明:前缀匹配下,返回最长匹配的 location,与 location 所在位置顺序无关
案例 5
server { listen 8080; server_name website.com; location ~ ^/doc[a-z]+ { return 701; } location ~ ^/docu[a-z]+ { return 702; } } curl -I website.com:8080/document 返回 HTTP/1.1 701
把顺序换一下:
server { listen 8080; server_name website.com; location ~ ^/docu[a-z]+ { return 702; } location ~ ^/doc[a-z]+ { return 701; } } curl -I website.com:8080/document 返回 HTTP/1.1 702
说明:可见正则匹配是使用文件中的顺序,先匹配成功的返回
其他 location 配置相关
匹配问号后的参数
请求 URI 中问号后面的参数是不能在 location 中匹配到的,这些参数存储在$args
变量中,可以用if
来判断。
例如,下面的配置匹配带有 tag 参数的 URI。
location / { if ($args ~ tag=) { # 如果地址匹配到 tag= } }
命名 location
带有@
的 location 是用来定义一个命名的 location,这种 location 不参与请求匹配,一般用在内部定向。用法如下:
location / { try_files $uri $uri/ @custom } location @custom { # ...do something }
上例中,当尝试访问 URI 找不到对应的文件就重定向到我们自定义的命名 location(此处为 custom)。
值得注意的是,命名 location 中不能再嵌套其它的命名 location。
location 实际使用建议
所以实际使用中,个人觉得至少有三个匹配规则定义,如下:
-
第一个必选规则:直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。
这里是直接转发给后端应用服务器了,也可以是一个静态首页:
location = / { proxy_pass http://tomcat:8080/index } -
第二个必选规则是处理静态文件请求,这是 nginx 作为 http 服务器的强项,有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用:
location ^~ /static/ { root /webroot/static/; } location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ { root /webroot/res/; } -
第三个规则就是通用规则,用来转发动态请求到后端应用服务器:
location / { proxy_pass http://tomcat:8080/ }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示