Nginx的location配置规则梳理

Nginx几乎是当下绝大多数公司在用的web应用服务,熟悉Nginx的配置,对于我们日常的运维工作是至关重要的,下面就Nginx的location配置进行梳理:

1)location匹配的是nginx的哪个变量?

$request_uri

2)location的匹配种类有哪些?

格式:location [ 空格 | = | ~ | ~* | !~ | !~* | @ ] /uri/ {}
解释:
=   表示精确匹配,如果找到,立即停止搜索并立即处理此请求。
~   表示执行一个正则匹配,区分大小写匹配
~*  表示执行一个正则匹配,不区分大小写匹配
!~  区分大小写不匹配
!~* 不区分大小写不匹配
^~  即表示只匹配普通字符(空格)。使用前缀匹配,^表示“非”,即不查询正则表达式。如果匹配成功,则不再匹配其他location。
@   指定一个命名的location,一般只用于内部重定向请求。例如 error_page, try_files
/   表示通用匹配,任何请求都会匹配到

------------------------------------------------------------------------
对应示例说明:
1)=
server {
  server_name wangshibo.com;
  location = /abcd {
  […]
  }
}
匹配情况:
    http://wangshibo.com/abcd        # 正好完全匹配
    http://wangshibo.com/ABCD        # 如果运行 Nginx server 的系统本身对大小写不敏感,比如 Windows ,那么也匹配
    http://wangshibo.com/abcd?param1?m2    # 忽略查询串参数(query string arguments),这里就是 /abcd 后面的 ?param1?m2
    http://wangshibo.com/abcd/    # 不匹配,因为末尾存在反斜杠(trailing slash),Nginx 不认为这种情况是完全匹配
    http://wangshibo.com/abcde    # 不匹配,因为不是完全匹配

2)(None)
可以不写 location modifier ,Nginx 仍然能去匹配 pattern 。这种情况下,匹配那些以指定的 patern 开头的 URI,注意这里的 URI 只能是普通字符串,不能使用正则表达式。
server {
  server_name website.com;
  location /abcd {
  […]
  }
}
匹配情况:
    http://wangshibo.com/abcd        # 正好完全匹配
    http://wangshibo.com/ABCD        # 如果运行 Nginx server 的系统本身对大小写不敏感,比如 Windows ,那么也匹配
    http://wangshibo.com/abcd?param1?m2    # 忽略查询串参数(query string arguments),这里就是 /abcd 后面的 ?param1?m2
    http://wangshibo.com/abcd/    # 末尾存在反斜杠(trailing slash)也属于匹配范围内
    http://wangshibo.com/abcde    # 仍然匹配,因为 URI 是以 pattern 开头的

3)~
这个 location modifier 对大小写敏感,且 pattern 须是正则表达式
server {
  server_name wangshibo.com;
  location ~ ^/abcd$ {
  […]
  }
}
匹配情况:
    http://wangshibo.com/abcd        # 完全匹配
    http://wangshibo.com/ABCD        # 不匹配,~ 对大小写是敏感的
    http://wangshibo.com/abcd?param1?m2    # 忽略查询串参数(query string arguments),这里就是 /abcd 后面的 ?param1?m2
    http://wangshibo.com/abcd/    # 不匹配,因为末尾存在反斜杠(trailing slash),并不匹配正则表达式 ^/abcd$
    http://wangshibo.com/abcde    # 不匹配正则表达式 ^/abcd$
注意:对于一些对大小写不敏感的系统,比如 Windows ,~ 和 ~* 都是不起作用的,这主要是操作系统的原因。

4)~*
与 ~ 类似,但这个 location modifier 不区分大小写,pattern 须是正则表达式
server {
  server_name website.com;
  location ~* ^/abcd$ {
  […]
  }
}
匹配情况:
    http://wangshibo.com/abcd        # 完全匹配
    http://wangshibo.com/ABCD        # 匹配,这就是它不区分大小写的特性
    http://wangshibo.com/abcd?param1?m2    # 忽略查询串参数(query string arguments),这里就是 /abcd 后面的 ?param1?m2
    http://wangshibo.com/abcd/    # 不匹配,因为末尾存在反斜杠(trailing slash),并不匹配正则表达式 ^/abcd$
    http://wangshibo.com/abcde    # 不匹配正则表达式 ^/abcd$

5)^~
匹配情况类似 2. (None) 的情况,以指定匹配模式开头的 URI 被匹配,不同的是,一旦匹配成功,那么 Nginx 就停止去寻找其他的 Location 块进行匹配了(与 Location 匹配顺序有关)

6. @
用于定义一个 Location块,且该块不能被外部Client 所访问,只能被Nginx内部配置指令所访问,比如try_files 或 error_page
	location @resize {
            rewrite ^/(.*)/cache/(.*)?(.*)$ /resize.php?dir=$1&path=$2$3;
            rewrite ^/(.*)/orgi/cert/(.*)?(.*)$ /pass/resize?dir=$1&type=cert&path=$2$3&is_orgi=true;
            rewrite ^/(.*)/orgi/card/(.*)?(.*)$ /pass/resize?dir=$1&type=card&path=$2$3&is_orgi=true;
            rewrite ^/(.*)/orgi/(.*)/(.*)?(.*)$ /pass/resize?dir=$1&type=$2&path=$3$4&is_orgi=true;
            include fastcgi_params;
        }

如下示例:

#通用匹配
location / {
	  root /var/www/web/;
	  autoindex on;
	  autoindex_exact_size off;
	  autoindex_localtime on;
	  access_log /var/www/log/nginx/access.log;
	  error_log /var/www/log/nginx/error.log;
}

#正则匹配
#proxy the php scripts to php-fpm
location ~ \.php(.*)$  {
        root /var/www/web/;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_read_timeout 300;
        }

#精确匹配
location = /hello.php {
		root /var/www/web/;
		rewrite ^(.*)$ http://www.wangshibo.com  redirect;
}


产生的效果如下:
访问根目录/,匹配到location /
访问除hello.php之外的其它php程序,匹配到location ~ \.php$,并且用php5-fpm去运行
访问hello.php,匹配到location = /hello.php,访问被重定向到http://www.wangshibo.com

3)location搜索优先级顺序如何?

精确匹配 > 字符串匹配( 长 > 短 [ 注: ^~ 匹配则停止匹配 ]) > 正则匹配( 上 > 下 )
 
在nginx的location和配置中location的顺序没有太大关系。正location表达式的类型有关。相同类型的表达式,字符串长的会优先匹配。
 
优先级排列:
1)等号类型(=)的精确匹配优先级最高,精确匹配只能命中一个。一旦匹配成功,则不再查找其他匹配项。
2)^~类型表达式,即字符串匹配,使用匹配最长的最为匹配结果。一旦匹配成功,则不再查找其他匹配项。
3)正则表达式类型(~ ~*)的优先级次之。如果有多个location的正则能匹配的话,则使用正则表达式最长的那个。
4)常规字符串匹配类型。按前缀匹配。
 
特别注意:
字符串匹配优先搜索,但是只是记录下最长的匹配 (如果 ^~ 是最长的匹配,则会直接命中,停止搜索正则),然后继续搜索正则匹配,如果有正则匹配,则命中正则匹配,如果没有正则匹配,则命中最长的字符串匹配.
 
实例说明:
1)先来测试下区分大小写和不区分大小写的优先级.如下:
 
   location ~ /5b.txt {
          return 501
   }
 
   location ~* /5b.txt {
          return 504
   }  
 
测试结果为:
http://192.168.1.80/5b.txt ------------501
http://192.168.1.80/5B.txt ------------504
 
将顺序反下,将~*放前面
   location ~* /5b.txt {
          return 501
   }
 
   location ~ /5b.txt {
          return 504
   }  
 
测试结果为:
http://192.168.1.80/5b.txt ------------501
http://192.168.1.80/5B.txt ------------501
 
结论: 去分和不区分大小写的正则匹配优先级相同,以先后顺序来决定匹配哪一个.
 
2)再来比较=与~的优先级
 
   location = /5b.txt {
          return 502
   }
 
   location ~ /5b.txt {
          return 504
   }  
 
测试结果为:
http://192.168.1.80/5b.txt -------------502
 
结论:=的优先级比~高
 
3)再来比较下 ^~ 与 ~的优先级
 
   location ~ /5b.txt {
          return 502
   }
 
   location ^~ /5b.txt {
          return 504
   }  
 
测试结果为:
http://192.168.1.80/5b.txt  --------------504
 
结论:^~的优先级比~高
 
 
4)再测试 ^~ 与 = 的优先级
 
   location ^~ /5b.txt {
          return 502
   }
 
   location = /5b.txt {
          return 504
   }  
 
测试结果:
http://192.168.1.80/5b.txt  --------------504
 
结论:=的优先级比 ^~高
 
5)再来测试^~同级之间的优先级
 
   location ^~ 5b.txt {
          return 502
   }
 
   location ^~ /3a/ {
          return 504
   }
 
 
测试结果:
http://192.168.1.80/5b.txt  --------------504
 
结论 :^~优先匹配的是从根开始的匹配
 
6)再来看空格(即什么都不加)与~的优先级比较
 
   location /5b.txt {
          return 502
   }
 
   location ~ /5b.txt {
          return 504
   }
 
测试结果为:
http://192.168.1.80/5b.txt  --------------504
 
结论:空格的优先级比~低
 
优先级排序为:
空格(即不加)
~与*~正则匹配的优先级按先后次序来决定的
^~同级之间的匹配是按照根目录顺序来的
 
------------------------------------------------------------------------------------------
再来看看下面的例子:
 
1)精确匹配,即“=”
location = /images/test.png {
    echo 'config1';
}
 
location  /images/test.png {
    echo 'config2';
}
 
location \/images\/test\.png$ {
    echo 'config3';
}
 
如果此时请求 http://127.0.0.1/images/test.png 输出的内容是config1, 毋容置疑,精确匹配优先级最高!
 
2)精确匹配的特殊情况
location = / {
    index index.html;
}
 
location / {
    echo 'config2';
}
 
此时输入http://127.0.0.1 输出的内容是config2, 怎么精确匹配的优先级不灵了呢?
是这样的,精确匹配还是起作用了,请求目录(非具体文件),nginx会将请求内部定向到index文件,
既此时真正的请求是http://127.0.0.1/index.html, 这是config2则被命中!
所以精确匹配不要用来匹配/
 
3)字符串搜索与正则搜索
location /images/test.png {
    echo 'config1';
}
 
location ^~ /images/ {
    echo 'config2';
}
 
location ~ \/images\/test\.png$ {
    echo 'config3';
}
 
location ~ \/images\/ {
    echo 'config4';
}
 
如果此时请求http://127.0.0.1/images/test.png 输出的内容是config3,正则命中。
(虽然config1为最长匹配的字符串,此时只做记录,后面还要搜索正则匹配,则config3正则匹配命中),
仔细观察可以发现config4也被匹配成功了,但是正则的匹配顺序是按照location的定义顺序匹配的,所以config3命中.
 
4)字符串匹配优先级的提升( ^~ )
location /images/ {
    echo 'config1';
}
 
location ^~ /images/test.png {
    echo 'config2';
}
 
location ~ /images/test\.png$ {
    echo 'config3';
}
 
location ~ \/images\/ {
    echo 'config4';
}
 
如果此时请求 http://127.0.0.1/images/test.png 输出的内容是config2, 首部匹配命中。
(因为字符串匹配是优先搜索的,此时发现config2 为最长的字符串匹配且为^~匹配方式,所以停止搜索正则,直接命中!)
 
所以这里的 ^~ 符号比较特殊,就是为了提高字符串匹配的优先级,优先于正则匹配.
------------------------------------------------------------------
/ 通用匹配,任何请求都会匹配到。
多个location配置的情况下,需要遵循:
首先匹配=
其次匹配^~
再其次按照配置文件的顺序进行正则匹配、
最后是交给/进行通用匹配
注意:当有匹配成功时,立刻停止匹配,按照当前匹配规则处理请求

看看下面匹配规则:
#规则A
location = / {
}

#规则B
location = /login {
}

#规则C
location ^~ /static/ {
}

#规则D
location ~ \.(gif|jpg|png|js|css)$ {
}

#规则E
location ~* \.png$ {
}

#规则F
location !~ \.xhtml$ {
}

#规则G
location !~* \.xhtml$ {
}

#规则H
location / {
}

那么产生的效果如下:
1)访问根目录/, 比如http://localhost/ 将匹配规则A
2)访问 http://localhost/login 将匹配规则B,http://localhost/register 则匹配规则H
3)访问 http://localhost/static/a.html 将匹配规则C
4)访问 http://localhost/a.gif, http://localhost/b.jpg 将匹配规则D和规则E,但是规则D顺序优先,规则E不起作用,而 http://localhost/static/c.png 则优先匹配到规则C
5)访问 http://localhost/a.PNG 则匹配规则E,而不会匹配规则D,因为规则E不区分大小写。
6)访问 http://localhost/a.xhtml 不会匹配规则F和规则G,http://localhost/a.XHTML不会匹配规则G,因为不区分大小写。规则F,规则G属于排除法,符合匹配规则但是不会匹配到,所以想想看实际应用中哪里会用到。
7)访问 http://localhost/category/id/1111 则最终匹配到规则H,因为以上规则都不匹配,这个时候应该是nginx转发请求给后端应用服务器,比如FastCGI(php),tomcat(jsp),nginx作为方向代理服务器存在。

注意:在实际使用中,至少清楚下面匹配规则
1)直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。
2)这里是直接转发给后端应用服务器了,也可以是一个静态首页

第一个必选规则:
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/;
}

第三个规则就是通用规则,用来转发动态请求到后端应用服务器
非静态文件请求就默认是动态请求,自己根据实际把握
毕竟目前的一些框架的流行,带.php,.jsp后缀的情况很少了
location / {
    proxy_pass http://tomcat:8080/
}

看看下面几个设置

# 重写跳转
rewrite  "^/conference/([^/]+)$" /con_detail.php?con_title=$1 last;
rewrite  "^/conference/([^/]+)/$" /con_detail.php?con_title=$1 last;

#屏蔽爬虫
if ($http_user_agent ~* "qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot|ia_archiver|Tomato Bot")
    {
    return 403;
    }

#favicon.ico不用打日志
   location = /favicon.ico {
      log_not_found off;
      access_log off;
    }

#不允许访问隐藏文件
location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

#访问图片,flash文件等不用打日志
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
    expires      7d; #文件返回的过期时间是7天
    access_log off;
}

#访问js和css文件不用打日志
location ~ .*\.(js|css)?$ {
    expires      1d; #文件返回的过期时间是1天
    access_log off;
}


#设置php-cgi
location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    #拦截不存在的php页面请求
    if (!-f $document_root$fastcgi_script_name) {
        return 404;
    }

}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏开源优测

移动测试Appium之API手册

移动测试Appium之API手册 前言 本文对Appium Python Client中webdriver.py代码进行分析说明。 笔者使用python3.6版...

4239
来自专栏微信公众号:Java团长

理解Java虚拟机体系结构

  众所周知,Java支持平台无关性、安全性和网络移动性。而Java平台由Java虚拟机和Java核心类所构成,它为纯Java程序提供了统一的编程接口,而不管下...

1366
来自专栏liulun

Nim教程【十一】

引用类型和指针类型 不同的引用可以只想和修改相同的内存单元 在nim中有两种引用方式,一种是追踪引用,另一种是非追踪引用 非追踪引用也就是指针,指向手动在内存中...

2156
来自专栏java思维导图

【一分钟知识】断言,Error和Exception区别

1、将GB2312编码的字符串转换为ISO-8859-1编码的字符串 ? 2、断言(assert) 软件开发中是一种常用的调试方式 断言用于保证程序最基本、关键...

3508
来自专栏大闲人柴毛毛

深入理解JVM(八)——类加载的时机

类的生命周期 一个类从加载进内存到卸载出内存为止,一共经历7个阶段: 加载——>验证——>准备——>解析——>初始化——>使用——>卸载 其中,类加载包括...

3405
来自专栏null的专栏

Python技巧——list与字符串互相转换

在Python的编程中,经常会涉及到字符串与list之间的转换问题,下面就将两者之间的转换做一个梳理。 1、list转换成字符串 命令:list() 例子: ?...

2673
来自专栏xingoo, 一个梦想做发明家的程序员

数字按照不同格式转换成字符串

  如果自己写函数,不使用itoa怎么判断呢?   我们用通常的办法,对数字进行每位的除商,得到后与字符'0'相加。 flag = 0; ...

21110
来自专栏Python小屋

Python正则表达式案例一则:单词非两端字符改为小写

问题描述:给定一段英文,要求把其中所有单词除两端字符之外的其他字母都改为小写。 技术要点: 1)re模块的sub()函数用法; 2)match对象的group(...

2936
来自专栏WebDeveloper

php缓冲区与header函数之间的秘密

我们在实际的开发中,是否听说过在header之前不能有任何的实际输出。甚至有的认为header函数必须写在代码的最前面。可是你是否试验过header函数之前输出...

1312
来自专栏自动化测试实战

RF-断言函数

5016

扫码关注云+社区

领取腾讯云代金券