Nginx rewirte规则详解-转于老僧系列Nginx


一、nginx rewrite  规则定义

使用 nginx 提供的全局变量或者自己设置的变量,结合正则表达式规则和标志位实现 url
重写以及重定向。

二、rewrite  作用域

rewrite 只能放在 sever{}, location {}, if{}中。

三、rewrite  作用对象

rewrite 规则只对域名后边的除去传递的参数外的字符串起作用。如果想对域名或者参
数字符串起作用,可以使用全局变量匹配。
例如:
http://www.laoseng.org/a/we/index.php?id=1&u=str 只对 /a/we/index.php 起作用。
针对域名使用全局变量$host.针对参数字符串$query_string.但都不是 rewrite 规则
if ($host ~* ^www\.(cafeneko\.info)) {
set $host_without_www $1;
rewrite ^(.*)$ http://$host_without_www$1 permanent;
}

四、使用格式用法

rewrite 正则字符串 替换字符串 flag 标志位;

五、正则表达式详解

5.1.  正则表达式特殊字符含义详解

正则字符  含义
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复 n 次
{n,} 重复 n 次或更多次
{n,m} 重复 n 到 m 次
*? 重复任意次,但尽可能少重复
+? 重复 1 次或更多次,但尽可能少重复
?? 重复 0 次或 1 次,但尽可能少重复
{n,m}? 重复 n 到 m 次,但尽可能少重复
{n,}? 重复 n 次以上,但尽可能少重复
\W
匹配任意不是字母,数字,下划线,汉字的
字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置

5.2.  正则表达式分类

5.2.1.  精确匹配

在匹配过程中匹配字符和要被匹配的字符能够划等号的。
例如:
(\d+)-abc-([0-9]+) 中的"-abc-"就是精确匹配。因为你匹配到的字符串必须包含"-abc-"
字符串。
而且你匹配成功的字符串中"-abc-"= 正则匹配中的"-abc-"字符串

5.2.2  模糊匹配

使用一些模糊代替的字符进行匹配。该字符能够代表很多字符。
例如:
. 表示任意字符可以匹配一个字母也可以匹配一个数字,或者其他的字符。
[0-9]表示匹配一个 0 到 9 之间任意一个数字。
[a-z]表示匹配一个 a 到 z 之间任意一个字母。

5.3.  反向引用

“rewrite 正则字符串 替换字符串 标志位“ 格式中,如果替换字符串重要用前边正则
表达式匹配到的字符,怎么处理?这时候就要用到反向引用。
正则字符串中模糊匹配到的字符串,可以在替换字符串中使用$n引用,其中$n 表示第
几个模糊匹配匹配到的字符串。
例如:
(\d+)-abc-([0-9]+)为例 在替代字符串中如果出现 $1 就表示 (\d+)匹配到的一串
数字,如果出现$2 表示([0-9]+)匹配到的一串数字,

六、flag  标志位

last 相当于 Apache 里的[L]标记,表示完成 rewrite
break 终止匹配, 不再匹配后面的规则
redirect 返回 302 临时重定向 地址栏会显示跳转后的地址
permanent 返回 301 永久重定向 地址栏会显示跳转后的地址
因为 301 和 302 不能简单的只单纯返回状态码,还必须有重定向的 URL,这就是 return 指令
无法返回 301,302 的原因了.
nginx 中分别使用 redirect 和 permanent 标志实现 301 和 302 状态以及跳转。
last 与 break 区别:
1.last 一般使用在非 location 中,break 一般使用在 location 中。
2.last 不终止重写后的 url 匹配,break 终止重写后的匹配
3.break 和 last 都能阻止继续执行后面的 rewrite 指令,
但是 last 如果在 location 下用的话,对于重写后的 URI 会重新匹配 location
但是 break 则不会重新匹配 location 。简单的说,break 终止的力度比 last 更加彻底

七、全局变量可以用做条件判断

$args #这个变量在读取时返回当前请求的 URL 参数串.
例如:
http://www.laoseng.org/index.php?id=1&user=laoseng,中
$arg 就等于 “id=1&user=laoseng”
$content_length 等于请求行的“Content_Length”的值。
content length 是指报头以外的内容长度。
一般的服务器实现中,超过这个长度的内容将被抛弃。 不会产生新 post。
$content_type 等同与请求头部的”Content_Type”的值
HTML 中的 ContentType, Content-Type,连接类型,一般是指网页中存在的
Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、
什么编码读取这个文件。
$document_root 等同于当前请求的 root 指令指定的值 ==== 在配置文件中 root
/var/www
$document_uri 与$uri 一样
$host 与请求头部中“Host”行指定的值或是 request 到达的 server 的名字(没有 Host 行)
一样
$limit_rate 允许限制的连接速率
$request_method 等同于 request 的 method,通常是“GET”或“POST”
$remote_addr 客户端 ip
$remote_port 客户端 port
$remote_user 等同于用户名,由 ngx_http_auth_basic_module 认证
$request_filename 当前请求的文件的路径名,由 root或 alias 和 URI request 组合而成
$request_body_file 给客户的 body 存成文件,并返回文件名
$request_uri 含有参数的完整的初始 URI
$query_string 与$args 一样
$server_protocol 等同于 request 的协议,使用“HTTP/1.0”或“HTTP/1.1”
$server_addr request 到达的 server 的 ip,一般获得此变量的值的目的是进行系统调用。为
了避免系统调用,有必要在 listen 指令中指明 ip,并使用 bind 参数。
$server_name 请求到达的服务器名
$server_port 请求到达的服务器的端口号
$uri 等同于当前 request 中的 URI,可不同于初始值,例如内部重定向时或使用 index
参数举例:
if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /msie/$1 break;

}


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

}


if ($request_method = POST ) {
return 405;

}


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

八、使用的指令

1.set 作用详解

set 作用定义变量或者设置变量。

2.if 作用详解

if主要用来判断一些在rewrite语句中无法直接匹配的条件,比如检测文件存在与否,http
header,cookie 等。
用法: if(条件) {„}
- 当 if 表达式中的条件为 true,则执行 if 块中的语句
- 当表达式只是一个变量时,如果值为空或者任何以 0 开头的字符串都会当作 false
- 直接比较内容时,使用 = 和 !=
- 使用正则表达式匹配时,使用
~ 为区分大小写匹配
~* 为不区分大小写匹配
!~和!~*分别为区分大小写不匹配及不区分大小写不匹配
总结一句: ~为正则匹配, 后置*为大小写不敏感, 前置!为”非”操作
文件及目录判断
-f 和!-f 用来判断是否存在文件
-d 和!-d 用来判断是否存在目录
-e 和!-e 用来判断是否存在文件或目录
-x 和!-x 用来判断文件是否可执行
举例:
if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /msie/$1 break;

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


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

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


if ($request_method = POST ) {
return 405;

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


if (!-f $request_filename) {
break;
proxy_pass http://127.0.0.1;
}//如果请求文件名不存在,则反向代理 localhost

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 提到)
(因为 301 和 302 不能简单的只单纯返回状态码,还必须有重定向的 URL,这就是 return
指令无法返回 301,302 的原因了. 作为替换,rewrite 可以更灵活的使用 redirect 和
permanent 标志实现 301 和302.)

4.break 作用详解

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

九、location  优先级问题

locatoin 并非像 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
例子来看看
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
为了简化问题最简单的方法就是把 rewrite 规则放在比 location 先执行的 server 里。

十、实战实例详例

1.网站目录首页处理

1.1.访问 index.html 统一转发到 /shouye/index.html 中
rewrite ^/index\.(html|htm)$ /shouye/index.html break;
1.2.不存在的文件 404 的全部跳转首页配置--固定目录访问指定该目录下的首页文件。
location / {
index index.html index.htm index.php;

root /application/www/laoseng;


if (-f $request_filename/index.html){ #某一个目录下的 html 可以访问。
rewrite (.*) $1/index.html break;

}


if (-f $request_filename/index.php){ #某个目录下的 php
rewrite (.*) $1/index.php;

}


if (!-f $request_filename){ #如果不存在转向访问首页 index.php
rewrite (.*) /index.php;
}
}

2.访问/abcjob/a.php 或者 a.xxx 转发访问 /area/abc/a.xxx permanent

地址栏不变: rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;
地址栏变化: rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 permanent;

3.多目录转成参数

www.laoseng.com/sort/2 => www.laoseng.com/index.php?act=sort&name=abc&id=2
if ($host ~* (.*)\.laoseng\.com) {
set $sub_name $1;
rewrite ^/sort/(\d+)/?$ /index.php?act=sort&cid=$sub_name&id=$1 last;
}

4.目录对换

/123456/xxxx -> /xxxx?id=123456
rewrite ^/(\d+)/(.+) /$2?id=$1 last;

5.根据浏览器类型访问不同目录

例如下面设定 nginx 在用户使用 ie 的使用重定向到/nginx-ie 目录下:
if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /nginx-ie/$1 break;
}
firefox 的用户访问 /nginx-firefox 下的文件
if ($http_user_agent ~ Firefox) {
rewrite ^(.*)$ /nginx-firefox/$1 break;
}
说明 ~ 意思是包含的意思.

6.目录自动加“/” 例如访问 http://www.laoseng.com/22 22是该域名下 22 目录。

if (-d $request_filename){
rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent;
}

7.安全或者特殊要求关于禁止访问那些文件或者禁止那些 ip 访问。

7.1 禁止访问某个目录下的单个文件
location ~ ^/dirname/file.html {
deny all;

break;
}
7.2 禁止访问某个或者多个目录
location ~ ^/(cron|templates)/ {
deny all;
break;
}
7.3.禁止访问某个目录下的可执行文件。
location ~ ^/dirname/(.*)\.(php|jsp|exe)$ {
deny all;
break;
}

8.给 favicon.ico 和 robots.txt设置过期时间;

这里为 favicon.ico 为 99 天,robots.txt 为 7 天并不记录 404 错误日志
location ~(favicon.ico) {
log_not_found off;
#access_log off;
expires 99d;
break;
}

9.将多级目录下的文件转成一个文件,增强 seo 效果

/job-123-456-789.html 指向/job/123/456/789.html
rewrite ^/job-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /job/$1/$2/jobshow_$3.html last;
其中的$n 就是指正则表达中模糊匹配的第 n 个匹配的字符串。
以上例子中$1 表示第一个([0-9]+)匹配的数字串,$2 表示第二个([0-9]+)匹配的数字串....

10.域名跳转

server
{
listen 80;
server_name jump.c1gstudio.com;
index index.html index.htm index.php;
root /opt/lampp/htdocs/www;
rewrite ^/ http://www.c1gstudio.com/;
access_log off;
}

11.多域名转向 www.c1gstudio.org www.c1gstudio.net 都转向 www.c1gstudio.com 域名

变化时候,使用老域名转向新域名
server_name www.c1gstudio.com www.c1gstudio.net;
index index.html index.htm index.php;
root /opt/lampp/htdocs;
if ($host ~ "c1gstudio/.net") {
rewrite ^(.*) http://www.c1gstudio.com$1 permanent;
}

12.三级域名跳转

if ($http_host ~* "^(.*)/.i/.c1gstudio/.com$") {
rewrite ^(.*) http://top.yingjiesheng.com$1;
break;
}

13.域名镜向

server
{
listen 80;
server_name www.abc.com;
index index.html index.htm index.php;
root /opt/lampp/htdocs/www;
rewrite ^/(.*) http://www.laoseng.com/$1 last;
access_log off;
}

14.文件反盗链并设置过期时间(其中域名是 www.laoseng.org)

这里的 return 490 为自定义的 http 状态码,默认为 403,方便找出正确的盗链的请求
“rewrite ^/ http://leech.c1gstudio.com/leech.gif;”显示一张防盗链图片
“access_log off;”不记录访问日志,减轻压力
“expires 3d”所有文件 3 天的浏览器缓存
location ~* ^.+\.(jpg|jpeg|gif|png|swf|rar|zip|css|js)$ {
valid_referers none blocked *.laoseng.org *.laoseng.net localhost 208.97.167.194;
if ($invalid_referer) {
rewrite ^/ http://www.laoseng.org/logo.gif;
return 490;
break;
}
access_log off;
root /var/www/laoseng;
expires 3d;
break;
}

15.针对传递参数的 rewrite 规则

动态参数 rewrite
以 discuz7.2 到 discuzx1.5 为例
if ($query_string ~* tid=([0-9]+)) {
set $id $1;
rewrite  "^(.*)/viewthread.php$"
$1/forum.php?mod=viewthread&tid=$id&extra=page%3D&page=1 last;
}
if ($query_string ~* gid=([0-9]+)) {
set $id $1;
rewrite "^(.*)/index.php$" $1/forum.php?gid=$id last;
}

16.针对访问 nginx 嵌套 if 变相实现方法

nginx 不支持 if and 和多层嵌套 if,需要通过其它

方法实现.
下面是把访问镜像网站 blog.laoseng.com 的爬虫转到 www 站.
set $needrewrite ''; #随便定义的一个变量
if  ($http_user_agent  ~*
(baiduspider|googlebot|soso|bing|sogou|yahoo|sohu-search|yodao|YoudaoBot|robozil
la|msnbot|MJ12bot|NHN|Twiceler)) {
set $needrewrite 'o';
}
if ($host ~ blog\.laoseng\.com) {
set $needrewrite "${needrewrite}k";
}
if ($needrewrite = ok) {
#return 403;
rewrite ^(.*) http://www.laoseng.com$1 permanent;
}

17.完整正确的 Discuz!在 Nginx 下的 Rewrite 如下:

rewrite ^/archiver/((fid|tid)-[\w\-]+\.html)$ /archiver/index.php?$1 last;
rewrite ^/forum-([0-9]+)-([0-9]+)\.html$ /forumdisplay.php?fid=$1&page=$2 last;
rewrite
^/thread-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /viewthread.php?tid=$1&extra=page%3D$3&page
=$2 last;
rewrite ^/space-(username|uid)-(.+)\.html$ /space.php?$1=$2 last;
rewrite ^/tag-(.+)\.html$ /tag.php?name=$1 last;
break;

十一、学习建议以及总结

做了 20 多条rewrite 规则,那么总结最终的想法以及学习 rewrite 规则的时候建议:
1.学习正则表达式。
2.学习一下 nginx 全局变量,以及自定义变量,if 语句等
3.实战配置一些例子,配置 10-20 种不同类型后,看看效果。
4.总结一下,思考一下.

十二、参考文献

http://wiki.nginx.org/HttpRewriteModule
http://blog.hexu.org/archives/1052.shtml
http://blog.c1gstudio.com/archives/434


十三:特别说明

转载自:老僧系列  nginx 之 之 rewrite  规则

猜你喜欢

转载自blog.csdn.net/it_faquir/article/details/76889673