Nginx的Location以及Rewrite配置规则

背景

Location和Rewrite规则的写法在日常的Nginx配置中很常见,有必要对其有一个了解。在学习这些知识之前,我们首先需要对正则表达式有一个简单的了解,可以参考之前的博文,这里就不再详细赘述了。

location

location指令用于匹配client请求uri的path部分,然后对不同的请求提供不同的静态内容,或者通过反向代理重定向到内部的server。

对于从client过来的request, nginx会进行预处理,nginx首先对采用 ’%XX’(uri采用%+十六进制格式用于在浏览器和插件中显示非标准的字母和字符) 格式文本编码的uri进行解码。然后处理path中的相对路径符号’.’和‘..’,然后对于含有相邻的两个及以上的’/’压缩成一个’/’。 这样处理完后会得到一个干净的path,然后会用这个path会在server的location指令的参数进行匹配。

关于它的配置详情,我们可以参考对应的Nginx官方文档,这里简单叙述。

location的正则匹配主要包括如下几种:

Nginx是按照如下顺序进行规则匹配的:

  1. 精确匹配 = 对应的字符串,如果匹配到,则停止匹配。
  2. 匹配 ^~ 开头的字符串前缀,如果匹配到,则停止匹配。
  3. 按照最长原则匹配剩下所有的常规字符串,如果有完全匹配,则直接使用终止。否则,将匹配记下来,继续下一步。
  4. 匹配所有正则表达,按照它们在配置文件中定义的顺序。 如果匹配到了,那就使用它,否则就使用第三步记下的匹配规则。

这个匹配过程翻译成优先级后则顺序如下(自上而下,优先级从高到低):

我们举个实际的例子来验证研究一下:

首先,我在我的MAC笔记本上安装Nginx,并配置如下配置文件:

如上,然后我建立每个location配置对应的root目录结构,方便验证测试:

每个index.html里面的内容,主要是输出标识他所属的目录,比如:

接下来启动Nginx并且绑定host就可以验证了,比如:

%e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2018-01-12-%e4%b8%8b%e5%8d%884-45-26

这样就可以在浏览器输入网址来测试location匹配了。 经过测试,我们得出如下结论:

最后,我们附上一个日常的基本Nginx的location写法:

Rewrite

Nginx的Rewrite规则主要实现url重写以及重定向,在这个过程中可以使用nginx提供的全局变量或自己设置的变量。Rewrite只能对域名后边的除去传递的参数外的字符串起作用

比如下面这个URL:

Rewrite只对 wp-admin/post.php 重写。 如果你想对域名或参数字符串起作用,可以使用全局变量匹配,也可以使用proxy_pass反向代理。当然如果URI中含有参数(post=1085&action=edit),那么默认情况下参数会被自动附加到替换厚的串上。如果你不想让那些参数跟在你改写后的uri后面,你可以通过在替换串的末尾加上?标记来解决这一问题。比如:

rewrite ^/users/(.*)$   /show?user=$1?   last;

这里我们要区分Location和Rewrite的区别,虽然他俩貌似都能实现跳转功能,但是本质上是不同的。 location是对路径做控制访问或反向代理,也可以proxy_pass到其他机器。rewrite是更改某一域名获取资源的路径。很多情况下rewrite也会写在location里,它们的执行顺序是(其实很简单,从外到里):

  1. 执行server块的rewrite指令
  2. 执行location匹配
  3. 执行选定的location中的rewrite指令

Rewrite的基本语法我们可以从对应的官方网站上看到,这里简单介绍一下:

Syntax: rewrite regex replacement [flag];
Default:
Context: server, location, if

在 regex 中匹配小括号()之间匹配的内容,可以在后面的表达式中通过$1来引用,$2表示的是前面第二个()里的内容,依次类推。

flag 标志位

Rewrite指令的最后一项参数为flag标记,主要包括:

  • last :表示完成本次rewrite,用重写后的URI继续进行 Location 匹配。
  • break:表示完成本次rewrite,并且直接应用,不再继续进行 Location 匹配。
  • redirect:返回一个302的临时重定向,地址栏会显示跳转后的地址。
  • permanent:返回一个301的永久重定向,地址栏会显示跳转后的地址。

这里我们举个例子来说明一下last和break的区别:

如果我在Nginx的配置文件中这样写,那么最终会造成死循环匹配,最后超过10次后就返回500错误码了。 因为我们将URI改写后last指令并不能限制它继续进行Location匹配,它还是会匹配中这个规则。 如果我们把last换成break就不会存在这个问题了。

if指令

在使用Rewrite规则时,我们可以对给定的条件condition进行判断。如果为真,大括号内的rewrite指令将被执行。他的语法规则特别简单,就和C语言一样:if(condition){…}  ,if条件(conditon)可以是如下任何内容:

  • 当表达式只是一个变量时,如果值为空或任何以0开头的字符串都会当做false
  • 直接比较变量和内容时,使用=!=
  • ~正则表达式匹配,~*不区分大小写的匹配,!~区分大小写的不匹配

-f!-f用来判断是否存在文件
-d!-d用来判断是否存在目录
-e!-e用来判断是否存在文件或目录
-x!-x用来判断文件是否可执行

我们来看一些常见的例子:

下面是可以用作if判断的全局变量:

比如,我们的这个网址:

http://www.foreverlakers.com/wp-admin/post.php?post=1085&action=edit

  • $host:www.foreverlakers.com
  • $server_port:80
  • $request_uri:http://www.foreverlakers.com/wp-admin/post.php
  • $document_uri:wp-admin/post.php
  • $document_root:/var/www/html/wordpress
  • $request_filename:/var/www/html/wordpress/wp-admin/post.php

 

 

参考

http://nginx.org/en/docs/http/ngx_http_core_module.html#location

http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite

https://segmentfault.com/a/1190000002797606

 

 

发表评论