Nginx Rewrite基础教程

文章目录

Nginx中rewrite规则是很多人头痛的问题,最近tennfy就被codeigniter在nginx中的伪静态问题折磨了挺长时间。因此,tennfy在这篇文章介绍下nginx rewrite基础教程。

Nginx rewrite基本语法

Nginx的rewrite语法其实很简单.主要用到setifreturn
breakrewrite等命令。下面来挨个介绍下这几个命令。

1.set

set主要是用来设置变量。

2.if

if主要用来判断一些在rewrite语句中无法直接匹配的条件,比如检测文件存在与否,http header,cookie等。

其用法为:

用法:if(条件) {…}

- 当if表达式中的条件为true,则执行if块中的语句

- 当表达式只是一个变量时,如果值为空或者任何以0开头的字符串都会当作false

- 直接比较内容时,使用 = 和 !=

- 使用正则表达式匹配时,使用

      ~ 大小写敏感匹配 
      ~* 大小写不敏感匹配 
      !~ 大小写敏感不匹配 
      !~* 大小写不敏感不匹配 

- 使用-f,-d,-e,-x检测文件和目录
     
      -f 检测文件存在
      -d 检测目录存在
      -e 检测文件,目录或者符号链接存在
      -x 检测文件可执行

举例分析一下:

1
2
3
if ($http_user_agent ~ MSIE) {
  rewrite  ^(.*)$  /msie/$1  break;
}

如果UA包含”MSIE”,rewrite 请求到/msie目录下

1
2
3
if ($http_cookie ~* "id=([^;] +)(?:;|$)" ) {
  set  $id  $1;
}

如果cookie匹配正则,设置变量$id等于正则引用部分

1
2
3
if ($request_method = POST ) {
  return 405;
}

如果提交方法为POST,则返回状态405 (Method not allowed)

1
2
3
4
if (!-f $request_filename) {
  break;
  proxy_pass  http://127.0.0.1;
}

如果请求文件名不存在,则反向代理localhost

1
2
3
if ($args ~ post=140){
  rewrite ^ http://example.com/ permanent;
}

如果query string中包含”post=140″,永久重定向到example.com

3.return

return可用来直接设置HTTP返回状态,比如403,404等(301,302不可用return返回,这个下面会在rewrite提到)。

4.break

立即停止rewrite检测,跟下面讲到的rewrite的break flag功能是一样的,区别在于前者是一个语句,后者是rewrite语句的flag。

5.rewrite

这是最为重要的知识点

用法: rewrite 正则 替换 标志位

其中标志位有四种

break     – 停止rewrite检测,也就是说当含有break flag的rewrite语句被执行时,
             该语句就是rewrite的最终结果
last      – 停止rewrite检测,但是跟break有本质的不同,last的语句不一定是最终结果,
             这点后面会跟nginx的location匹配一起提到
redirect  – 返回302临时重定向,一般用于重定向到完整的URL(包含http:部分)
permanent – 返回301永久重定向,一般用于重定向到完整的URL(包含http:部分)

因为301和302不能简单的只单纯返回状态码,还必须有重定向的URL,这就是return指令无法返回301,302的原因了。作为替换,rewrite可以更灵活的使用redirect和permanent标志实现301和302。

举例说明:

1
rewrite  ^(/download/.*)/media/(.*)\..*$  $1/mp3/$2.mp3  last;

如果请求为 /download/eva/media/op1.mp3,则请求被rewrite到 /download/eva/mp3/op1.mp3。

rewrite在使用中还有很多值得注意的地方:

1)rewrite的生效区块为sever, location, if;

2)rewrite只对相对路径进行匹配,不包含hostname ;

3)使用相对路径rewrite时,会根据HTTP header中的HOST跟nginx的server_name匹配后进行rewrite,如果HOST不匹配或者没有HOST信息的话则rewrite到server_name设置的第一个域名,如果没有设置server_name的话,会使用本机的localhost进行rewrite;

4)前面提到过,rewrite的正则是不匹配query string的,所以默认情况下,query string是自动追加到rewrite后的地址上的,如果不想自动追加query string,则在rewrite地址的末尾添加?,如下所示

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

Nginx location 和 rewrite retry

1、location基础

用过nginx的朋友都知道location区块,location区块有点像Apache中的RewriteBase,但对于nginx来说location是控制的级别而已,里面的内容不仅仅是rewrite。location是nginx用来处理对同一个server不同的请求地址使用独立的配置的方式。

语法规则: location [=|~|~*|^~] /uri/ { … }

=       开头表示精确匹配

^~      开头表示uri以某个常规字符串开头,理解为匹配 url路径即可。nginx不对url做编码,
        因此请求为/static/20%/aa,可以被规则^~ /static/ /aa匹配到(注意是空格)。

~       开头表示区分大小写的正则匹配

~*      开头表示不区分大小写的正则匹配

!~和!~* 分别为区分大小写不匹配及不区分大小写不匹配 的正则

/       通用匹配,任何请求都会匹配到。

2、location匹配命中规则

server区块中如果有包含rewrite规则,则会最先执行,而且只会执行一次,然后再判断命中哪个location的配置,再去执行该location中的rewrite,当该location中的rewrite执行完毕时,rewrite并不会停止,而是根据rewrite过的URL再次判断location并执行其中的配置。

location并非像rewrite那样逐条执行,而是有着匹配优先级的,当一条请求同时满足几个location的匹配时,其只会选择其一的配置执行。其寻找的方法为:

1. 首先寻找所有的常量匹配,如location /, location /av/, 以相对路径自左向右匹配,匹配长度
   最高的会被使用

2. 然后按照配置文件中出现的顺序依次测试正则表达式,如 
   location ~ download\/$, location ~* \.wtf, 第一个匹配会被使用 

3. 如果没有匹配的正则,则使用之前的常量匹配

而下面几种方法当匹配时会立即终止其他location的尝试

1. = 完全匹配,location = /download/ 

2. ^~ 终止正则测试,如location ^~ /download/ 如果这条是最长匹配,则终止正则测试,这个符号
   只能匹配常量 

3. 在没有=或者^~的情况下,如果常量完全匹配,也会立即终止测试,比如请求为 /download/ 会完全
   命中location /download/而不继续其他的正则测试

总结:

1. 如果完全匹配(不管有没有=),尝试会立即终止

2. 以最长匹配测试各个常量,如果常量匹配并有 ^~, 尝试会终止
 
3. 按在配置文件中出现的顺序测试各个正则表达式 

4. 如果第3步有命中,则使用其匹配location,否则使用第2步的location

另外还可以定义一种特殊的named location,以@开头,如location @thisissparta 不过这种location定义不用于一般的处理,而是专门用于try_file、error_page的处理,这里不再深入。

举例说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
location  = / {
  ....配置A
}
 
location  / {
  ....配置B
}
 
location ^~ /images/ {
  ....配置C
}
 
location ~* \.(gif|jpg|jpeg)$ {
  ....配置D
}

访问 / 会使用配置A -> 完全命中
访问 /documents/document.html 会使用配置B -> 匹配常量B,不匹配正则C和D,所以用B
访问 /images/1.gif 会使用配置C -> 匹配常量B,匹配正则C,使用首个命中的正则,所以用C
访问 /documents/1.jpg 会使用配置D -> 匹配常量B,不匹配正则C,匹配正则D,使用首个命中的正则,所以用D

3、rewrite retry

这里就存在一个问题,如果rewrite写的不正确的话,是会在location区块间造成无限循环的。所以nginx才会加一个最多重试10次的上限。比如这个例子

1
2
3
location /download/ {
  rewrite  ^(/download/.*)/media/(.*)\..*$  $1/mp3/$2.mp3  last;
}

如果请求为 /download/eva/media/op1.mp3,则请求被rewrite到 /download/eva/mp3/op1.mp3,虽然rewrite的结果重新命中了location /download/,虽然这次并没有命中rewrite规则的正则表达式,但因为缺少终止rewrite的标志,其仍会不停重试download中rewrite规则直到达到10次上限返回HTTP 500。

这里需要介绍一下last标志位与break标志位的区别。

- break  终止当前location的rewrite检测,而且不再进行location匹配 
- last   终止当前location的rewrite检测,但会继续重试location匹配并处理区块中的rewrite规则

举例说明:

1
2
3
4
5
location /download/ {
  rewrite  ^(/download/.*)/media/(.*)\..*$  $1/mp3/$2.mp3  ;
  rewrite  ^(/download/.*)/movie/(.*)\..*$  $1/avi/$2.mp3  ;
  rewrite  ^(/download/.*)/avvvv/(.*)\..*$  $1/rmvb/$2.mp3 ;
}

如果请求为 /download/acg/moive/UBW.avi

last的情况是: 在第2行rewrite处终止,并重试location /download..死循环
break的情况是: 在第2行rewrite处终止,其结果为最终的rewrite地址。

虽然如此,但是很多情况下还是需要使用last。典型的例子就是wordpress的permalink rewrite。

WordPress的Permalink rewrite实现

常见的情况下, wordpress的rewrite是放在location /下面,并将请求rewrite到/index.php,这时如果这里使用break就会出现问题,因为nginx返回的是没有解释的index.php的源码。

这里一定要使用last才可以在结束location / 的rewrite,并再次命中location ~ \.php$,将其交给fastcgi进行解释。其后返回给浏览器的才是解释过的html代码。

一般在网上见到的nginx下wordpress固定链接的rewrite写法为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
location / {
     if (-d $request_filename){
           rewrite ^/(.*)([^/])$ /$1$2/ permanent;
     }
     if (-f $request_filename/index.html){
          rewrite (.*) $1/index.html break;
     }
     if (-f $request_filename/index.php){
          rewrite (.*) $1/index.php;
     }
     if (!-f $request_filename){
           rewrite (.*) /index.php;
     }
}

对于上面的rewrite规则进行简单的介绍:当访问文件或者目录时,如果都不存在,所有请求都重定向到index.php,这样一来,我们可以访问网站上的已经存在的文件或目录,对于实际不存在的文件则交给index.php来处理。当请求交到index.php时,如果请求的是文档的固定链接,则可以查询数据库,返回文章,如果不是,则可以用index.php来生成更加友好的404页面,而不是服务器自带的404页面。

实际上上面的写法还是有一些冗余,我们可以使用try_files来简化写法,首先来简单介绍下try_files的语法规则:

1、try_files uri1 uri1 … urin
   逐个判断资源是否存在,有则返回,没有则继续找下一个资源

2、try_files uri1 uri2 … =404
   逐个判断资源是否存在,有则返回,没有则继续找下一个资源,如果都没有,则返回404

这个指令正好满足wordpress伪静态的需求:当访问一个资源时,首先查看它是否在服务器上存在,如果不存在,则查看相同名称的文件夹是否存在,如果都不存在,则交给wordpress的index.php处理。所以,最终我们只需要一句代码就可以实现wordpress的伪静态:

1
2
3
location / {
     try_files $uri $uri/ /index.php; 
}

参考文章:Nginx Rewrite研究笔记

本文出自 TENNFY博客,转载时请注明出处及相应链接。

本文永久链接: https://www.tennfy.com/3606.html

下一篇文章:

上一篇文章:

2人参与了讨论

  1. Hustus 说:

    这个不错,收藏了。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

1 + 3 = ?


您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

返回顶部