Nginx rewrite模块深入浅出详解

https://www.cnblogs.com/beyang/p/7832460.html

rewrite模块(ngx_http_rewrite_module)

nginx通过ngx_http_rewrite_module模块支持url重写、支持if条件判断,但不支持else。另外该模块需要PCRE支持,应在编译nginx时指定PCRE支持。根据相关变量重定向和选择不同的配置,从一个location跳转到另一个location,不过这样的循环最多可以执行10次,超过后nginx将返回500错误。同时,重写模块包含set指令,来创建新的变量并设其值,这在有些情景下非常有用的,如记录条件标识、传递参数到其他location、记录做了什么等等。学习rewrite之前要对正则表达式要很熟悉,下面先给出一些常用的正则表达式元字符。

 
1
2
3
4
5
6
7
8
9
10
11
.     :匹配除换行符以外的任意字符
?     :重复0次或1次
+     :重复1次或更多次
*     :重复0次或更多次
\d    :匹配数字
^     :匹配字符串的开始字符
$     :匹配字符串的结束字符
{n}   :重复n次
{n,}  :重复n次或更多次
[c]   :匹配单个字符c
[a-z] :匹配a-z小写字母的任意一个

在rewrite中,如果使用小括号(),那么在小括号之间匹配的内容,可以在后面通过$1来引用,$2表示的是前面第二个()里的内容,后面会说到。

Rewrite模块指令

1)break

 
1
2
3
Syntax: break;
Default:—
Context:server, location, if

此指令的意思是停止执行当前虚拟主机的后续rewrite指令集。使用示例如下:

 
1
2
3
4
if ($slow) {
     limit_rate 10k;
     break;
}

2)if

 
1
2
3
Syntax: if (condition) { ... }
Default:—
Context:server, location

对给定的条件(condition)进行判断,如果条件为真,大括号内的rewrite指令将被执行。

条件(conditon)可以是如下任何操作:

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

2. 使用“=”和“!=”比较一个变量和字符串;

3. 使用“~”做正则表达式匹配,“~*”做不区分大小写的正则匹配,“!~”做区分大小写的正则不匹配;

4. 使用“-f”和“!-f” 检查一个文件是否存在;

5. 使用“-d”和“!-d”检查一个目录是否存在;

6. 使用“-e”和“!-e”检查一个文件、目录、符号链接是否存在;

7. 使用“-x”和“ !-x”检查一个文件是否可执行;

如下示例:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//如果UA包含"MSIE",rewrite请求到/msid/目录下
if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$ /msie/$1 break;
}
 
//如果cookie匹配正则,设置变量$id等于正则引用部分
if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
    set $id $1;
}
 
//给某个访问IP返回403
if ( $remote_addr = "202\.38\.78\.85" ){
    return 403;
}
 
//如果提交方法为POST,则返回状态405(Method not allowed)。return不能返回301,302
if ($request_method = POST) {
    return 405;
}
 
//如果$slow可以通过set指令设置,则进行限速处理
if ($slow) {
    limit_rate 10k;
}
 
//如果请求的文件名不存在,则反向代理到localhost 。这里的break也是停止rewrite检查
if (!-f $request_filename){
    break;
    proxy_pass http://127.0.0.1;
}
 
//如果query string中包含"post=140",则永久重定向到example.com
if ($args ~ post=140){
    rewrite ^ http://example.com/ permanent;
}
 
//防盗链
location ~* \.(gif|jpg|png|swf|flv)$ {
    valid_referers none blocked www.baidu.com www.ywnds.com;
    if ($invalid_referer) {
        return 404;
    }
}

3)return

 
1
2
3
4
5
Syntax: return code [text];
        return code URL;
        return URL;
Default:—
Context:server, location, if

停止处理并为客户端返回状态码,非标准的444状态码将关闭连接,不发送任何响应头。可以使用的状态码有:204,400,402-406,408,410, 411, 413, 416与500-504。如果状态码附带文字段落,该文本将被放置在响应主体。相反,如果状态码后面是一个URL,该URL将成为location头部值。没有状态码的URL将被视为一个302状态码。

4)rewrite

 
1
2
3
Syntax: rewrite regex replacement [flag];
Default:—
Context:server, location, if

rewrite指令的功能就是,使用nginx提供的全局变量或自己设置的变量,然后结合正则表达式和标志位实现url重写以及重定向。rewrite指令只能放在server、location或if中,并且只能对域名后边的除去传递的参数外的字符串起作用,例如 http://ywnds.com/a/we/index.php?id=1&u=str,只对/a/we/index.php重写,语法如上面所示。

如果想对域名或参数字符串起作用,可以使用全局变量匹配,也可以使用proxy_pass反向代理。

或许看rewrite和location功能有点像,都能实现跳转,主要区别在于rewrite是在同一域名内更改获取资源的路径,而location是对一类路径做控制访问或反向代理,可以proxy_pass到其他机器。很多情况下rewrite也会写在location里,它们的执行顺序是:

1)处理在server级别中定义的模块指令;

2)为请求查找location;

3)处理在选中的location中定义的模块指令,如果指令改变了URI,按新的URI查找location。这个循环至多重复10次,之后nginx返回错误500 (Internal Server Error);

如果一个URI匹配了rewrite指令指定的正则表达式(regex),则URI就按照replacement进行重写,而rewrite按配置文件中出现的顺序执行。其中flag标志可以停止继续处理。如果replacement以”http://”或”https://”开始,将不再继续处理,那么这个重定向将直接返回给客户端。

flag可以是如下参数:

  • last,完成该rewrite规则的执行后,停止处理后续rewrite指令集;然后查找匹配改变后URI的新location;
  • break,完成该rewrite规则的执行后,停止处理后续rewrite指令集,并不再重新查找;但是当前location内剩余非rewrite语句和location外的的非rewrite语句可以执行;
  • redirect,返回302临时重定向,地址栏会显示跳转后的地址;
  • permanent,返回301永久重定向,地址栏会显示跳转后的地址;即表示如果客户端不清理浏览器缓存,那么返回的结果将永久保存在客户端浏览器中了。

因为301和302不能简单的只返回状态码,还必须有重定向的URL,这就是return指令无法返回301,302的原因了。这里last和break区别有点难以理解:

1)last一般写在server和if中,而break一般使用在location中;

2)last不终止重写后的url匹配,即新的url会再从server走一遍匹配流程,而break终止重写后的匹配;

3)break和last都能组织继续执行后面的rewrite指令。

示例:

 
1
2
3
4
5
6
7
server {
     ...
     rewrite ^(/download/.*)/media/(.*)..*$ $1/mp3/$2.mp3 last;
     rewrite ^(/download/.*)/audio/(.*)..*$ $1/mp3/$2.ra last;
     return 403;
     ...
}

如果这些rewrite放到“/download/”路径,那么location如下所示,这时应使用break而不是last,使用last将循环10次匹配,然后返回500错误:

 
1
2
3
4
5
location /download/ {
     rewrite ^(/download/.*)/media/(.*)..*$ $1/mp3/$2.mp3 break;
     rewrite ^(/download/.*)/audio/(.*)..*$ $1/mp3/$2.ra break;
     return 403;
}

对于重写后的URL(replacement)包含原请求的请求参数,原URL的?后的内容。如果不想带原请求的参数,可以在replacement后加一个问号。如下,我们加了一个自定义的参数user=$1,然后在结尾处放了一个问号?,把原请求的参数去掉。

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

注: 对花括号“}”或“;”来说,使用时需要用双引号或单引号包围。因为他们既能用在重定向的正则表达式里,也是用在配置文件里分割代码块, 为了避免冲突, 正则表达式里带花括号的话,应该用双引号(或者单引号)包围。比如,要将类似以下的URI:”/photos/123456″重定向到”/path/to/photos/12/1234/123456.png”可以用以下方法 (注意双引号):

 
1
rewrite "/photos/([0-9] {2})([0-9] {2})([0-9] {2})" /path/to/photos"

5)set

 
1
2
3
Syntax: set $variable value;
Default:—
Context:server, location, if

定义一个变量并赋值,值可以是文本,变量或者文本变量混合体。

6)rewrite_log

 
1
2
3
Syntax: rewrite_log on | off;
Default:rewrite_log off;
Context:http, server, location, if

开启或关闭以notice级别打印rewrite处理日志到error log文件。nginx打开rewrite log的例子如下:

 
1
2
rewrite_log on;                        //打开rewrite log
error_log logs/xxx.error.log notice;   //把error log的级别调整为notice

7)uninitialized_variable_warn

 
1
2
3
Syntax: uninitialized_variable_warn on | off;
Default:uninitialized_variable_warn on;
Context:http, server, location, if

控制是否输出为初始化的变量到日志。

重写规则组成部分

第一部分:任何重写规则的第一部分都是一个正则表达式

正则表达式可以使用括号来捕获,后续可以根据位置来将其引用,位置变量值取决于捕获正则表达式中的顺序,$1引用第一个括号中的值,$2引用第二个括号中的值,以此类推。如:

 
1
^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$

$1是两个小写字母组成的字符串,$2是由小写字母和0到9的数字组成的5个字符的字符串,$3将是个文件名,$4是png、jpg、gif中的其中一个。

第二部分:重写规则的第二部分是URI

当重写规则第一部分被匹配到了之后,则请求被改写,那么该URI可能包含正则表达式中的捕获的位置参数或这个级别下的nginx任何配置变量。如:

 
1
/data?file=$3.$4

如果这个URI不匹配nginx配置的任何location,那么将给客户端返回301(永久重定向)或302(临时重定向)的状态码来表示重定向类型。该状态码可以通过第三个参数来明确指定。

第三部分:重写规则的第三部分就是标记(flag)

第三部分也就是尾部的标记(flag), last标记将导致重写后的URI搜索匹配nginx的其他location,最多可循环10次。如:

 
1
rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4 last;

全局变量

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

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$args               #这个变量等于请求行中的参数,同$query_string;
$content_length     #请求头中的Content-length字段;
$content_type       #请求头中的Content-Type字段;
$document_root      #当前请求在root指令中指定的值,如:root /var/www/html;
$host               #请求主机头字段,否则为服务器名称;
$http_user_agent    #客户端agent信息;
$http_cookie        #客户端cookie信息;
$limit_rate         #这个变量可以限制连接速率;
$request_method     #客户端请求的动作,通常为GET或POST;
$remote_addr        #客户端的IP地址;
$remote_port        #客户端的端口;
$remote_user        #已经经过Auth Basic Module验证的用户名;
$request_filename   #当前请求的文件路径,由root或alias指令与URI请求生成;
$scheme             #HTTP方法(如http,https);
$server_protocol    #请求使用的协议,通常是HTTP/1.0或HTTP/1.1;
$server_addr        #服务器地址,在完成一次系统调用后可以确定这个值;
$server_name        #服务器名称;
$server_port        #请求到达服务器的端口号;
$request_uri        #包含请求参数的原始URI,不包含主机名,如:”/foo/bar.php?arg=baz”;
$uri                #不带请求参数的当前URI,$uri不包含主机名,如”/foo/bar.html”;
$document_uri       #与$uri相同,例:http://localhost:88/test1/test2/test.php;

例如:http://localhost:88/test1/test2/test.php这个URL,其中:

 
1
2
3
4
5
6
$host:localhost
$server_port:88
$request_uri:http://localhost:88/test1/test2/test.php
$document_uri:/test1/test2/test.php
$document_root:/var/www/html
$request_filename:/var/www/html/test1/test2/test.php

Rewrite模块使用实例

1)使用rewrite指令把访问80端口的请求重定向到443

对于HTTPS网站,一般最少需要定义两个虚拟主机,一个是使用80的虚拟主机,另一个就是使用443端口的虚拟主机了。比如在浏览器输入www.ywnds.com(http://www.ywnds.com)会自动跳转到https://www.ywnds.com。

 
1
rewrite ^(.*)$ https://www.ywnds.com$uri redirect;

这种跳转就是302临时重定向跳转,如果把flag变成permanent就成了301永久重定向跳转了。

2)作为重写规则的一部分,传递新的查询字符串参数是使用重写规则的目标之一

 
1
rewrite ^/images/(.*)_(\d+)x(\d+)\.(png|jpg|gif)$ /resizer/$1.$4?width=$2&height=$3? last;

3)使用rewrite模块禁止用户代理

Nginx可以通过各种方式来限制访问,例如NGINX基本Http认证、allow/deny等等,这些都是前文提过的,下面来看看nginx如何通过用户代理来禁止访问。

user agent是什么?

简单来说告诉服务器你当前使用的是什么浏览器、工具等来访问我的。例如火狐、chrome、wget、curl等浏览器或工具。使用$http_user_agent变量就可以获取到用户代理,一般在定义日志格式时都会使用这个变量,把用户代理记录到日志中去。

如何禁止特定UA?

我们不希望被使用wget或者curl来下载我的文件,怎么做呢?这里就可以使用rewrite模块了,编辑nginx配置文件,以下内容放在http配置段,那么整个nginx都生效。如果放到server里,那么一个域名生效,你放哪,哪就有效!

 
1
2
3
if ($http_user_agent ~* (curl) ) {
    return 404;
}

禁止多个UA

 
1
2
3
if ($http_user_agent ~* (wget|curl) ) {
    return 404;
}

nginx重写规则说起来挺简单的,做起来就难,重点在于正则表达式,同时,还需要考虑到nginx执行顺序。

[转帖]Nginx rewrite模块深入浅出详解的更多相关文章

  1. Nginx rewrite模块深入浅出详解

    rewrite模块(ngx_http_rewrite_module) nginx通过ngx_http_rewrite_module模块支持url重写.支持if条件判断,但不支持else.另外该模块需要 ...

  2. [转帖]nginx服务器安装及配置文件详解

    nginx服务器安装及配置文件详解 http://seanlook.com/2015/05/17/nginx-install-and-config/  发表于 2015-05-17 |  更新于: 2 ...

  3. [转帖]Nginx安装及配置详解 From https://www.cnblogs.com/zhouxinfei/p/7862285.html

    Nginx安装及配置详解   nginx概述 nginx是一款自由的.开源的.高性能的HTTP服务器和反向代理服务器:同时也是一个IMAP.POP3.SMTP代理服务器:nginx可以作为一个HTTP ...

  4. 【转】nginx服务器安装及配置文件详解

    原文:http://seanlook.com/2015/05/17/nginx-install-and-config/ nginx服务器安装及配置文件详解 nginx在工作中已经有好几个环境在使用了, ...

  5. 2-4、nginx特性及基础概念-nginx web服务配置详解

    Nginx Nginx:engine X 调用了libevent:高性能的网络库 epoll():基于事件驱动event的网络库文件 Nginx的特性: 模块化设计.较好扩展性(不支持模块动态装卸载, ...

  6. 【第六课】Nginx常用配置下详解

    目录 Nginx常用配置下详解 1.Nginx虚拟主机 2.部署wordpress开源博客 3.部署discuz开源论坛 4.域名重定向 5.Nginx用户认证 6.Nginx访问日志配置 7.Ngi ...

  7. Linux中Nginx安装与配置详解

    转载自:http://www.linuxidc.com/Linux/2016-08/134110.htm Linux中Nginx安装与配置详解(CentOS-6.5:nginx-1.5.0). 1 N ...

  8. Nginx简介及配置文件详解

    http://blog.csdn.net/hzsunshine/article/details/63687054 一 Nginx简介    Nginx是一款开源代码的高性能HTTP服务器和反向代理服务 ...

  9. Nginx 主配置文件参数详解

    Nginx 主配置文件参数详解 Nginx 安装完毕后,会有响应的安装目录,安装目录里 nginx.conf 为 nginx 的主配置文件, ginx 主配置文件分为 4 部分,main(全局配置). ...

随机推荐

  1. 【工具大道】ssh登录Linux服务器,并显示图形化界面

    本文地址 点击关注微信公众号 "程序员的文娱情怀" 分享提纲: 1. 概述 2. mac版实现ssh登录,显示图形化 1. 概述 平时ssh登录到Linux服务器都是在命令行下进行 ...

  2. jquery中prop()和attr()用法

    jquery1.6中新加了一个方法prop(),一直没用过它,官方解释只有一句话:获取在匹配的元素集中的第一个元素的属性值. 大家都知道有的浏览器只要写disabled,checked就可以了,而有的 ...

  3. 经常在比特币中看到的merkle树是什么?

    区块基础-merkle树   Merkle tree中文叫做梅克尔树,这当然不是一棵真正的植物树,merkle tree是计算机数据结构中的一种树,是由计算机科学家 Ralph Merkle 提出的, ...

  4. UVA1608-Non-boring sequences(分治)

    Problem UVA1608-Non-boring sequences Accept: 227  Submit: 2541Time Limit: 3000 mSec Problem Descript ...

  5. 转://ASM与文件系统之间文件传输

    熟悉数据库运维的程序猿都知道,数据的备份重于一切,随着业务的发展,数据量也会越来越大,有时候备份集会放在文件系统上面,有的备份集会放在asm存储上面,实现文件系统到文件系统之间的文件传输很简单,cp或 ...

  6. python3 内置函数

    '''iter()和next()'''# lst = [1, 2, 3]# it = iter(lst)# print(it.__next__())# print(it.__next__())# pr ...

  7. day07----字符编码解码、文件操作(1)

    字符编码: 什么是字符编码? 字符编码是将人识别的字符转换成计算机能识别的二进制字符(01),转换的规则就是编码表. 人能识别的字符串  与  计算机能识别的二进制字符 两者之间对应关系构成的结构称为 ...

  8. 计算机网络#关于HDLC#

    HDLC(High-Level Data Link Control,高级数据链路控制):面向比特型的协议 主站(发送命令).从站(响应命令).复合站(既可发送命令,也可响应命令) HDLC链路配置方式 ...

  9. Java多线程(三)—— synchronized关键字详解

    一.多线程的同步 1.为什么要引入同步机制 在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源.必须对这种潜在资源冲突进行预防. 解决方法:在线程使用一个资源时为其加锁即可. 访问资 ...

  10. zookeeper核心-zab协议-《每日五分钟搞定大数据》

    上篇文章<paxos与一致性>说到zab是在paxos的基础上做了重要的改造,解决了一系列的问题,这一篇我们就来说下这个zab. zab协议的全称是ZooKeeper Atomic Bro ...