1.摘要
nginx Rewrite规则可以让网站的url中达到某种状态时定向/跳转到某个规则,本文具体介绍这些规则和说明。
2. 内容
2.1 Rewrite规则
rewrite功能就是,使用nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向。rewrite只能放在server{}, location{}, if{}中,并且只能对域名后边的除去传递的参数外的字符串起作用,例如 http://seanlook.com/a/we/index.php?id=1&u=str 只对/a/we/index.php重写。
语法:
rewrite regex replacement [flag];
如果相对域名或参数字符串起作用,可以使用全局变量匹配,也可以使用proxy_pass反向代理。
表面看rewrite和location功能有点像,都能实现跳转,主要区别在于rewrite是在同一域名内更改获取资源的路径,而location是对一类路径做控制访问或反向代理,可以proxy_pass到其他机器。很多情况下rewrite也会写在location里,它们的执行顺序是:
- 1,执行server块的rewrite指令
- 2,执行location匹配
- 3,执行选定的location中的rewrite指令
如果其中某步URI被重写,则重新循环执行1-3,直到找到真实存在的文件;循环超过10次,则返回500 Internal Server Error错误。
2.2 flag标志位
- last : 相当于Apache的[L]标记,表示完成rewrite
- break : 停止执行当前虚拟主机的后续rewrite指令集
- redirect : 返回302临时重定向,地址栏会显示跳转后的地址
- permanent : 返回301永久重定向,地址栏会显示跳转后的地址
(1)break和last区别
rewrite里面flag中break和last是什么区别?请看下面的例子:
root /opt/app/code;
location ~ ^/break {
rewrite ^/break /test/ break;
}
location ~ ^/last {
rewrite ^/last /test/ last;
}
location /test/ {
default_type application/json;
return 200 '{"status":"success"}';
}
break是停止处理当前的ngx_http_rewrite_module指令集,就是说不会在向下匹配新的location,last停止处理当前的ngx_http_rewrite_module指令集并开始搜索与更改的URI匹配的新位置;
假设我们在页面上访问的url是http://walidream.com/break,根据上面的nginx规则,肯定会匹配到第一个,匹配到第一个之后,根据rewrite正则,url由原来的/break变为/test/但是在root/opt/code/目录下,没有找到/test/目录下面的内容,所以nginx会返回404错误码。
如果将http://walidream.com/break换成http://walidream.com/last,根据上面的nginx规则,会匹配到第二个location,根据location里面rewrite正则,url由原来的/last变为/test/,但是会重新
匹配loction中,可以简单理解就是将替换后urlhttp://walidream.com/test重新发起一次请求。这个时候会匹配到第三个loaction,根据location里的rewrite正则,会返回200码,并且会返回json内容’{“status”:“success”}’。
总结说明下:
- last一般写在server和if中,而break一般使用在location中;
- last不终止重写后的url匹配,即新的url会再从server走一遍匹配流程,而break终止重写后的匹配;
- break和last都能组织继续执行后面的rewrite指令;
(2)redirect和permanent区别
rewrite里面flag中redirect(临时重定向)和permanent(永久重定向)是什么区别?请看下面的例子:
location ~ ^/imooc {
rewrite ^/imooc http://www.imooc.com/ redirect;
}
location ~ ^/wali {
rewrite ^/wali http://www.imooc.com/ permanent;
}
redirect会返回带有302代码的临时重定向,permanent会返回带有301代码的永久重定向。
假设我们在页面上访问url是http://walidream.com/imooc,根据nginx匹配规则会匹配到第一个location,会重写url,返回一个带有302状态代码,重写后的url是http://www.imooc.com,浏览器会重定向到这个网址,
当我们再次访问http://walidream.com/imooc,nginx还是会先匹配再替换然后重定向。
如果在页面上输入http://walidream.com/wali,根据nginx匹配规则会匹配到第二个location,会重写url,返回一个带有301状态码,重写后的url是http://www.imooc.com,浏览器会重新定向到这个网址,
当我们再次访问http://walidream.com/wali,这个时候浏览器根本不会在经过nginx,而是直接有浏览器重定向到这个网址。
2.3 全局变量
内置的全局变量如下。
变量名 | 描述 |
---|---|
$args | 这个变量等于请求行中的参数,同$query_string |
$content_length | 请求头中的Content-length字段 |
$content_type | 请求头中的Content-Type字段 |
$document_root | 当前请求在root指令中指定的值 |
$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相同 |
2.4 if指令与全局变量使用
if判断指令的语法
if (condition) { ... }
对给定的条件condition进行判断。如果为真,大括号内的rewrite指令将被执行,if条件(conditon)可以是如下任何内容:
(1) 当表达式只是一个变量时,如果值为空或任何以0开头的字符串都会当做false
(2) 直接比较变量和内容时,使用=或!=
(3) 正则表达式匹配,*不区分大小写的匹配,!~区分大小写的不匹配
(4) -f和!-f用来判断是否存在文件
(5) -d和!-d用来判断是否存在目录
(6) -e和!-e用来判断是否存在文件或目录
(7) -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)。return不能返回301,302
if ($slow) {
limit_rate 10k;
} //限速,$slow可以通过 set 指令设置
if (!-f $request_filename){
break;
proxy_pass http://127.0.0.1;
} //如果请求的文件名不存在,则反向代理到localhost 。这里的break也是停止rewrite检查
if ($args ~ post=140){
rewrite ^ http://example.com/ permanent;
} //如果query string中包含"post=140",永久重定向到example.com
location ~* \.(gif|jpg|png|swf|flv)$ {
valid_referers none blocked www.jefflei.com www.leizhenfang.com;
if ($invalid_referer) {
return 404;
} //防盗链
}
valid_referers 指令详解
该指令后面可以接 none blocked serevr_names string或者是正则表达式:
- none 代表没有referer
- blocked 代表有referer但是被防火墙或者是代理给去除了
- string或者正在表达式 用来匹配referer
nginx会通过查看referer字段和valid_referers后面的referer列表进行匹配,如果匹配到了就invalid_referer字段值为0 否则设置该值为1。
现我为公司写出了实现这一功能的代码
valid_referers none blocked server_names;
if ($invalid_referer) {
rewrite ^/ http://********.com/ redirect;
}
说明:
1.首先当我输入我要打开的网址的时候,因为是直接输入的没有referer所以匹配了valid_referers后面的none或者是blocked 所以invalid_referer值为0 所以不进行跳转.
当我是从这个网站里面的链接跳到该网站首页的时候 因为referer的值是肯定包含srever_names,所以匹配了server_names所以不进行跳转。
2.当我从搜素引擎进去的时候因为referer字段类似于www.google.com.hk/search开始进行匹配 发现没有一个匹配,则此时会设置invalid_referer值为1 if语句成功执行,进行了跳转. 达到功能。
2.4 调试rewrite是否正确
2.4.1 打开rewrite_log
nginx中要检测rewrite是否正确,就需要开启rewrite_log才能排错,rewrite_log会打印错误。检测完后,建议将rewrite_log关闭。
rewrite_log
Syntax: rewrite_log on | off;
Default: rewrite_log off;
Context: http, server, location, if
示例:
rewrite_log on;
error_log /var/log/nginx/rewrite.log notice;
2.4.2 利用变量或者return调试
一般来说,要验证rewrite正则是否正确,就要打开rewrite_log日志,如果匹配错了,日志会记录错误信息。
另外,一般的检测语法,我们可以利用变量return 返回。
location ~ ^/wali {
default_type application/json;
return 200 '{"status":"success"}';
}
location ~ ^/yagm {
default_type application/json;
return 200 '{"status":"error"}';
}
这样有时候在调试时会让我们变得稍微方便一点,还有nginx扩展模块如echo就能直接输出内容或者打印内部变量。
3. 参考
(1)Nginx系列教程之基于nginx配置请求转发location及rewrite规则详解
https://blog.csdn.net/JunyouYH/article/details/107469699
(2)nginx rewrite规则(23)
https://www.imooc.com/article/283363