Nginx的 location 指令用于匹配客户端请求的 URI,并定义了如何处理匹配成功的请求。它通常用于配置不同 URI 路径下的不同处理方式,如转发请求给后端服务器或提供静态文件服务。可根据需要配置多个 location 块以覆盖不同的URI匹配情况。
名词
-
文件型 URI:指对服务器上某个文件的访问形式,通常不以斜杠
/结尾,目的无非就是对文件的查看、下载等;例如
localhost/foo。 -
目录型 URI:指对服务器上某个目录的访问形式,通常以斜杠
/结尾,目的无非就是浏览目录列表(目录索引服务)或目录(项目)中的默认页面;例如
localhost/foo/。 -
Nginx 外部重定向:给客户端返回一个重定向 301 状态码的响应。需要客户端手动重定向,通常如果使用浏览器的话会自动跳转;
-
Nginx 内部重定向:Nginx 内部改写客户端访问的 URI,对改写后的 URI 进行重新匹配,客户端无感知;
配置语法
语法
location @name { ... }
location [ = | ~ | ~* | ^~ ] uri { ... }
默认值
无
上下文
server, location
命名 Location
使用 @ 命名一个 location,外部无法访问,只有在内部跳转时(例如 try_files,error_page 等)使用。
例如:请求 URI 资源不存在时跳转 @notfound 命名 location,返回 404 状态码。
location /foo {
root ...;
try_files $uri $uri/ @notfound;
}
location @notfound {
return 404;
}
匹配规则修饰符
-
默认(空白):字符串前缀匹配,只要请求 URI 开头部分与配置
uri相同即可; -
^~:字符串前缀匹配,与默认空白规则相似,只不过会阻断正则,可以理解^~为非正则,优先级比空白符更高; -
=:精确匹配,请求 URI 与 配置uri全等; -
~:区分大小写的正则匹配,如果运行 Nginx 的系统(如 Windows)对大小写不敏感,那么~*和~相同; -
~*:不区分大小写的正则匹配;
前缀按照长短匹配:前缀匹配按照匹配长度决定优先级,如果多个规则全部匹配,选择匹配长度最长的。
正则按照顺序匹配:正则匹配按照配置文件中的定义顺序,最先定义的优先级更高。
匹配流程描述
- 首先进行精确匹配(
=),匹配成功则终止其余匹配,使用该规则。 - 进行字符串前缀匹配(空白符与
^~),在所有满足前缀匹配的规则中选择匹配字符串最长的规则。 - 如果匹配字符串最长的规则的修饰符为
^~,则终止正则匹配,使用该规则。 - 如果匹配字符串最长的规则的修饰符为空白符,缓存该规则,进行正则匹配。
- 正则匹配,顺序按照配置文件中定义的顺序进行匹配。正则匹配成功则使用该规则。
- 正字匹配失败则使用 4 中缓存的规则。
精确匹配对请求处理提速:例如,频繁请求
/,可以定义location = /,将加快对这些请求的处理,如果精确匹配成功,将会终止其他搜索。
流程图
匹配规则后的响应逻辑
当前仅讨论一般情况(仅配置 root 或 alias,未配置 index、try_files、autoindex 等)。
-
当
uri参数配置为/foo时,例如location /foo { ... },Nginx 将foo视为文件或目录。如果规则匹配成功,Nginx 会查找名为
foo的文件是否存在:-
存在:将该文件作为响应返回(200 状态码);
-
不存在:查找名为
foo的目录是否存在:-
存在:重定向(301 状态码)目录型的 URI
/foo/; -
不存在:返回未找到(404 状态码);
-
-
-
当
uri参数配置为/foo/时,例如location /foo/ { ... },Nginx 将foo仅视为目录。如果规则匹配成功,Nginx 会查找名为
foo的目录是否存在:- 不存在:返回未找到(404 状态码);
- 存在:将当前 URI 与默认索引
index值(index.html)拼接为新的 URI 并对其进行内部重定向,重新查找匹配的规则:- 不存在匹配规则:返回未找到(404 状态码);
- 存在匹配规则:返回相应的处理结果;
对于
index、autoindex、try_files对一般情况的影响请往后看。
强调两个点:
-
当访问文件型 URI 匹配到目录时,Nginx 返回重定向(301 状态码),该重定向指向对应的目录型 URI,属于外部重定向,需要客户端自行重新访问。
例如. 客户端访问
localhost/foo的 URI,未找到foo文件,但匹配到foo的文件夹,于是 Nginx 会返回重定向(301 状态码),要求客户端重定向到localhost/foo/。 -
当访问目录型 URI 成功匹配时,Nginx 重新拼接包含默认索引的 URI,在 Nginx 内部重新对新的 URI 进行规则匹配,属于内部重定向。
例如. 客户端访问
localhost/foo/的 URI 并且匹配到foo文件夹,于是 Nginx 将 URI 与默认索引值重新拼接为localhost/foo/index.html并再次查找匹配规则,对于客户端来说,相当于一开始访问的就是localhost/foo/index.html。
流程图
index 配置
语法
index file ...;
默认值
index index.html;
上下文
http, server, location
index 用于指定当请求的 URI 匹配到目录时 Nginx 应该返回的默认索引文件,文件名可以包含变量,文件按指定的顺序进行检查,列表的最后一个元素可以是具有绝对路径的文件。
对一般情况的影响:配置 index 将会改变一般情况中拼接内部重定向的 URI 地址。
autoindex 配置
语法
autoindex on | off;
默认值
autoindex off;
上下文
http, server, location
autoindex 定义是否开启目录索引服务的配置项。
对一般情况的影响:开启后如果目录存在默认索引文件,则依然打开默认索引文件,如果不存在默认索引文件,则会展示目录列表。
try_files 配置
语法
try_files file ... uri;
try_files file ... =code;
默认值
无
上下文
server, location
try_files 按指定顺序检查配置的文件或目录(结尾 /)是否存在,配置值可以使用变量(例如 $uri)。所查找的文件或目录是基于 root 或 alias 路径,返回第一个找到的文件或目录,如果所有的文件或目录都不存在,会进行一个内部重定向到最后一个参数。
最后一个参数可以是:
- 一个内部重定向 URI,例如
/foo/index.html; - 一个错误代码,例如
=404; - 命名
location,例如@foo;
对一般情况的影响:当配置了 try_files,则不会查找客户端 URI 所请求的文件或目录是否存在,而是直接按照配置顺序查找 try_files 指定的文件或目录是否存在。当文件存在时响应该文件,当目录存在时对客户端响应外部重定向(301 状态码)至该目录。
使用 try_files 模拟一般情况:
location /foo {
root ...;
try_files $uri $uri/ =404;
}
上例中使用 $uri 变量模拟规则匹配后,先查找所请求的文件,如果文件不存在则查找目录,如果都不存在则响应未找到(404 状态码)。
FAQ
一个请求两条访问日志
当浏览器访问某 URI 时 access.log 中同时出现两条日志,为什么?
例如. 访问 localhost/foo 的日志
localhost - - [...] "GET /foo HTTP/1.1" 301 ...
localhost - - [...] "GET /foo/ HTTP/1.1" 200 ...
答:浏览器访问 localhost/foo,当匹配该 URI 的规则的 root 或 alias 下存在一个名为 foo 的目录而非文件时,Nginx 首先会将该 URI 转换为 localhost/foo/ 的目录型 URI 返回给浏览器一个重定向操作(也就是第一条访问日志,响应码 301)。浏览器接收到 Nginx 的重定向响应,通常会自动进行跳转,所以再次发起 localhost/foo/ 的请求(也就是第二条访问日志),根据再次匹配的结果进行响应(比如 200、404、403 等等)。
某些规则匹配成功但结果不尽人意
在某些情况下(下面会说明某些是什么)精确或正则匹配成功,却返回未找到(404 状态码)或意想不到的结果页面,为什么?
不妨尝试如下配置:
server {
listen 80;
server_name localhost;
location = /foo {
root /usr/share/nginx;
}
}
目录结构:
/usr/share/nginx
└── foo
└── index.html
当访问 localhost/foo 时,会返回未找到 404 状态码,如果将精确匹配的等号去掉,一切都正常了,是精确匹配不会查找默认页面吗?
答:访问 localhost/foo 后 Nginx 发现匹配的是 foo 目录,首先会返回 301 状态码的重定向至 localhost/foo/ 的响应,浏览器再次请求 localhost/foo/ 的目录型 URI,Nginx 内部重新拼接 URI 为 localhost/foo/index.html ,foo 中确实存在 index.html,一切看似都很正常,为什么会未找到?别忘记配置文件中仅有一条精确匹配的规则,Nginx 对内部重定向的 localhost/foo/index.html 会进行重新匹配,没有匹配项,所以返回未找到 404 状态码。同理如果将 = /foo 替换为 ~ /foo$ 也是一样的结果。
那么是哪些特殊情况呢?你的规则存在无法匹配 Nginx 内部重定向 URI 的情况。所以使用精确匹配与固定字符结尾(固定字符+$)的正则匹配规则时,小心这一点。
本文为原创内容,版权所有 © 2024 姚生。欢迎转载,但请注明出处,并附上原文链接。未经授权不得用于商业用途。如有疑问,请联系作者。