请求定位(Server模块)
nginx有两层指令来匹配请求 URL
:
- 第一个层次是 server 指令,它通过
域名、ip和端口
来做第一层级匹配,当找到匹配的 server 后就进入此 server 的 location 匹配。 - 第二个层次是location指令,它通过
请求uri
来做第二层匹配。
1 示范例子
server {
listen 81; # 监听的端口
server_name localhost; # 域名或ip
location / { # 访问路径配置
return 400;
}
location /xx/xx { # 访问路径配置
return 402;
}
}
server {
listen 82; # 监听的端口
server_name localhost; # 域名或ip
location / { # 访问路径配置
return 401;
}
}
注解:每一个端口为一个Server,用于做不同的事情。
补充知识点:
域名访问
1) windows
C:\Windows\System32\drivers\etc\hosts 在这个目录下增加域名与ip的映射
2) linux
/etc/hosts 在这个目录下增加域名和ip的映射关系
2 基于域名的例子
server {
listen 82;
server_name www.liyong.f.com;
location / {
return 401;
}
}
server {
listen 83;
server_name www.liyong.s.com;
location / {
return 402;
}
}
然后再服务客户端配置域名ip映射,windows和linux配置文件不同参考上面的路径。
访问:http://www.liyong.s.com:83/ http://www.liyong.f.com:82/
请求定位(Location )
每个server块中可以包含多个location块。location块的主要作用是,基于nginx服务器接收到的客户端发送过来的请求uri
(例如, server_name/uri-string
),对除虚拟主机名称(也可以是IP别名)之外的字符串(前例中“/uri-string
”部分)进行匹配,对特定的请求进行处理。
地址定向、数据缓存和应答控制等功能都是在这部分实现。许多第三方模块的配置也是在location块中提供功能。
location 语法
location [ = | ~ | ~* | ^~ ] /uri { #配置location可以配置的属性 } #[] 里面的意思是多种写法 | 表示或 不要错误的理解为location的配置还有[] 里面配一串之类的 实际配置 location =, location ^~ 是这样的
location匹配参数解释
参数 | 匹配方式 | 匹配模式 | 说明 | 注意事项 |
---|---|---|---|---|
= | 精准匹配 | 普通字符串匹配 | 用于标准uri 前,要求请求字符串与uri精准匹配,成功则立即处理,nginx停止搜索其他匹配。 | |
~ | 正则匹配 | 正则表达式匹配 | 用于正则uri ,表示uri包含正则表达式,并且区分大小写。 | 如果uri包含正则表达式,就必须要使用“~”或者“~*”标识。 |
~* | 正则表达式匹配 | 用于正则uri ,表示uri包含正则表达式,并且不区分大小写。 | ||
^~ | 带参前缀匹配 (短路匹配) | 普通字符串匹配 | 用于标准uri 前,并要求一旦匹配到就会立即处理,不再去匹配其他的正则URI,一般用来匹配目录。 | |
空 | 普通前缀匹配 | 普通字符串匹配 | location后没有参数直接跟着标准uri ,表示前缀匹配,代表跟请求中的uri从头开始匹配。 |
location匹配顺序
location 的匹配并不完全按照其在配置文件中出现的顺序来匹配,请求URI 会按如下规则进行匹配,优先级从高到低依次为(序号越小优先级越高
):
1. location = # 精准匹配,精准匹配成功则会立即停止其他类型匹配;
2. location ^~ # 带参前缀匹配。如果是带有 ^~ 的前缀匹配,匹配成功则立即停止其他类型匹配;如果是普通前缀匹配(不带参数 ^~ )成功则会暂存,继续查找正则匹配;
3. location ~ # 正则匹配(区分大小写)。当同时有多个正则匹配时,按其在配置文件中出现的先后顺序优先匹配,命中则立即停止其他类型匹配;
4. location ~* # 正则匹配(不区分大小写)
5. location /a # 普通前缀匹配,优先级低于带参数前缀匹配。所有正则匹配均未成功时,返回步骤 2 中暂存的普通前缀匹配(不带参数 ^~ )结果。普通字符串匹配则无视顺序,只会选择最精确的匹配。
6. location / # 任何没有匹配成功的,都会匹配这里处理
location 匹配规则优先级验证
1 通用匹配
相当于是兜底处理,所有的规则匹配不成功就会走兜底处理。
server {
listen 82;
server_name www.liyong.f.com;
# 因为所有的地址都以 /开头,所以这条规则将匹配到所有请求,比如访问 / 和 /testOrder , 则 / 匹配 /data,/test也匹配,
location / { #作为最后的匹配项
return 401;
}
location /testOrder { #普通前缀匹配
root html;
index index.html
}
}
访问:
http://www.liyong.f.com:82/data,http://www.liyong.f.com:82/test 都是401
访问testOrder 会走下面的匹配 其实它也是能匹配上/的,但是/testOrder优先级更高就会去服务器相对路径/html/testOrder/寻找资源。
同时通过上面的例子也验证了,匹配与location的书写顺序无关。如果有关就会被/ 先匹配。
2 普通前缀匹配
就是根据普通路径进行匹配,非等值匹配匹配最长的路径。
server {
listen 82;
server_name www.liyong.f.com;
location /doc {
return 402;
}
location /docu {
return 500;
}
}
访问:http://www.liyong.f.com:82/document
说明
:前缀匹配下,返回最长匹配的 location,与 location 所在配置文件中的顺序无关。
3 正则匹配
3.1 正则匹配与普通匹配
location ~ ^/proxy { #正则匹配
return 500;
}
location /proxy { #普通匹配
return 410;
}
访问:http://www.liyong.f.com:82/proxy 可以发现 正则匹配的优先级更高
3.2 正则匹配与正则匹配
location ~ ^/proxy[0-9]+ {
return 500;
}
location ~ ^/proxy[0-9]+ {
return 401;
}
访问:http://www.liyong.f.com:82/proxy114445
但是如果我们交换顺序:
location ~ ^/proxy[0-9]+ {
return 401;
}
location ~ ^/proxy[0-9]+ {
return 500;
}
访问:http://www.liyong.f.com:82/proxy114445
说明
:通过上面的案例可以看到,如果路径满足多个正则,则配置顺序决定了优先级,在前面location会匹配成功。
3.3 区分大小写的正则匹配
server {
listen 82;
server_name www.liyong.f.com;
location ~ /doc{
return 401;
}
location ~* /doc { #正则匹配区分大小写
return 402;
}
}
访问: www.liyong.f.com:82/DOC 402
访问 :www.liyong.f.com:82/doc 401
4 带参前缀匹配
4.1 多个带参前缀与正则的优先级
server {
listen 82;
server_name www.liyong.f.com;
location ^~ /doc {
return 401;
}
location ^~ /document {
return 402;
}
location ~ /document {
return 500;
}
}
http://www.liyong.f.com:82/document
说明
:都是带参的情况下也是会匹配最长路径,但是这点和正则不同,正则是谁先成功就就走谁。而且我们后面还配了一个路径更长的正则匹配确没有走,证实了带参前缀匹配 ^~ 命中以后会再搜寻其他带参前缀匹配,不会搜索正则匹配。
5 精准匹配
5.1 多个精准匹配
location = /doc {
return 401;
}
location = /document {
return 402;
}
精准匹配:
只有访问http://www.liyong.f.com:82/doc,http://www.liyong.f.com:82/document 才能正确处理。也就是路径必须一模一样。
5.2 精准匹配与带参前缀匹配
location ^~ /document {
return 402;
}
location = / {
return 400;
}
location = /document {
return 401;
}
访问:/document 返回401 所以精准匹配的优先级高于带参匹配
location配置运用的几个准则
1 第一个必选规则
直接匹配网站根
,通过域名访问网站首页比较频繁,使用这个会加速处理,比如说官网。- 可以是一个静态首页,也可以直接转发给后端应用服务器
location = /index.html {
root html
index index.html index.htm
}
location = / {
root html
index index.html index.htm
}
2 第二个必选规则
处理静态文件请求,这是nginx作为http服务器的强项
有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用
location ^~ /static/ {
root /webroot/static/;
}
location ~* .(gif|jpg|jpeg|png|css|js|ico)$ {
root /webroot/res/;
}
3 通用匹配,作为兜底
location / {
return 500;
}
location 的其它配置项
1 匹配问号后的参数
请求 URI 中问号后面的参数是不能在 location 中匹配到的,这些参数存储在 $query_string 变量中,可以用 if 来判断。
location /idx {
if ($query_string ~* ".*id.*" ) {
return 500;
}
return 401;
}
访问:http://www.liyong.s.com:83/idx?jdid 500
访问:http://www.liyong.s.com:83/idx?xx 401
2 URI 结尾带不带/
1)在location
中配置的URI
有没有 /
都没有影响。也就是说 /user/
和 /user
是一样的。
2)如果浏览中发送的 URI 结构是 https://domain.com/
的形式,尾部有没有 /
都不会造成重定向。因为浏览器在发起请求的时候,默认加上了 /
。虽然很多浏览器在地址栏里也不会显示 /
。这一点,可以访问baidu验证一下。
3)如果浏览中发送的 URI 结构是 https://domain.com/``some-dir/
。尾部如果缺少 /
将导致重定向。因为根据约定,URI 尾部有 /
表示目录,没有 /
表示文件。
a. 所以访问 /some-dir/
时,服务器会自动去该目录下找对应的默认文件。
b. 如果访问 /some-dir
的话,服务器会先去找 some-dir
文件,找不到的话会将 some-dir
当成目录,重定向到 /some-dir/
,去该目录下找默认文件。
rewrite指令
rewrite功能就是,使用nginx提供的全局变量或自己设置的变量,结合正则表达式和标记位实现URL重写以及重定向。
比如:更换域名后需要保持旧的域名能跳转到新的域名上、某网页发生改变需要跳转到新的页面、网站防盗链等等需求。
- rewrite只能放在
server{},location{},if{}
中,并且默认只能对域名后边的除去传递的参数外的字符串
起作用。
rewrite 跳转实现
-
nginx 是通过
ngx_http_rewrite_module
模块支持 url 重写、支持 if 条件判断,但不支 持 else。 -
另外该模块需要
PCRE
支持,应在编译 nginx 时指定 PCRE 支持,默认已经安装。 -
根据相关变量重定向和选择不同的配置,从一个 location 跳转到另一个 location,不过这样 的循环最多可以执行
10
次,超过后 nginx 将返回 500 错误。
rewrite 执行顺序 -
执行 server 块里面的 rewrite 指令。
-
执行 location 匹配。
-
执行选定的 location 中的 rewrite 指令。
flag标记说明
last
:本条规则匹配完成后,不终止重写后的url匹配,一般用在server
和if
中。break
:本条规则匹配完成即终止,终止重写后的url匹配,一般使用在location
中。redirect
:返回302
临时重定向,浏览器地址会显示跳转后的URL地址。permanent
:返回301
永久重定向,浏览器地址栏会显示跳转后的URL地址。
示范例子
1 重新定向到nginx中的路径
server {
listen 82;
server_name www.liyong.f.com;
location / {
# 判断主机名为www.itxh.com则重写为www.newitxh.com
if ($host = 'www.liyong.f.com'){
rewrite ^/(.*)/(.*)$ /demo/$2 permanent; #这里$2 表示第二个括号的内容
}
root html;
index index.html index.htm;
}
location /demo/aaa {
alias html;
index index.html index.htm;
}
}
访问:http://www.liyong.f.com:82/test/aaa 可以看到被重新定向
结合这个uri解释一下$2 这个$2就是aaa,如果我们写$1则值是test
拓展:
location /test/ {
# 判断主机名为www.itxh.com则重写为www.newitxh.com
if ($host = 'www.liyong.f.com'){
rewrite ^/(.*)/(.*)$ /demo/$2 permanent;
}
root html;
index index.html index.htm;
}
location /demo/aaa {
alias html;
index index.html index.htm;
}
这个时候我们访问路径:http://www.liyong.f.com:82/test/test/aaa 才能狗重定向到 /demo/aaa 。由此我们验证了rewrite 后面的正则表达式^/(.)/(.)$是不包含location的路径的,是除去location路径向后匹配。就比如http://www.liyong.f.com:82/test/aaa/bbb被重定向以后是http://www.liyong.f.com:82/demo/bbb
2 基于域名的跳转
rewrite ^/(.*)$ http://www.liyong.demo.com/$1 permanent;
3 结合ip 或者其它内置变量使用(以ip为例子)
错误配置并且浏览器会缓存重定向,如果你修改了nginx配置发现依然重定向到之前的地址,可以清空浏览缓存试试。
server {
listen 82;
server_name www.liyong.f.com;
set $is_rewrite false;
if ($remote_addr = '15.195.14.17') {
set $is_rewrite true;
}
if ($is_rewrite = true) {
rewrite (.+) /demo/aaa permanent ;
}
location /test/ {
if ($host = 'www.liyong.f.com'){
add_header X-Remote-Addr $remote_addr;
rewrite ^/(.*)/(.*)$ /demo/$2 permanent;
}
root html;
index index.html index.htm;
}
location /demo/aaa {
alias html;
index index.html index.htm;
}
}
这个配置会导致死循环,这个是实战中一定要注意的,但是实际上重定向超过一定次数也就不会再发送请求到nginx了,会报错误重定向次数过多
正确的配置:
server {
listen 80;
server_name localhost;
# 设置变量为$rewrite,变量值为boolean类型的true
set $rewrite true;
# 当客户端IP为15.195.14.17时,将$rewrite变量值设为false
if ($remote_addr = "15.195.14.17"){
set $rewrite false;
}
# 当变量值为true时,进行rewrite重写,将域名后面的路径重写为/weihu.html
if ($rewrite = true){
rewrite (.+) /weihu.html;
}
# 精确匹配/weihu.html请求
location = /weihu.html {
# 看到400页面,就表示重定向成功了
return 400;
}
location / {
return 401;
}
}
4 基于参数匹配的跳转
4.1
server {
listen 80;
server_name localhost;
#正则匹配成功就跳转
if ($request_uri ~ ^/100-(100|200)-(\d+).html$) {
rewrite (.+) http://www.liyong.com permanent;
}
location / {
return 400;
}
}
4.2
server {
listen 80;
server_name localhost;
location ~* ^/abc/123.html {
rewrite (.+) http://www.liyong.com permanent
}
location / {
return 400;
}
}
4.3
server {
listen 80;
server_name localhost;
location ~* ^/abc/123.html {
rewrite (.+) http://www.liyong.com permanent
}
location / {
return 400;
}
}