10人参与 • 2025-03-05 • Redis
rewrite是nginx服务器提供的一个重要功能,用于实现url的重写。
例如我们访问https://aa.qq.com,打开的是https://age.qq.com/,这就是使用url重写的特性来实现的。
ngx_http_rewrite_module
为实现url重写提供了指令支持。
官方文档地址:https://nginx.org/en/docs/http/ngx_http_rewrite_module.html
接下来我们来看看rewrite的相关指令。
作用域:server, location, if
语法:set $variable value;
该指令可以设置一个变量。
$variable
:为变量的名称,可以看到变量的名称以$
符号开头,且不要与nginx预设的全局变量名相同。value
:为变量的值,可以是字符串、其他变量或者两者的组合。既然自定义的变量名不能与nginx的全局变量名相同,那就有必要了解使用rewrite功能时常用的nginx全局变量。
rewrite常用全局变量
全局变量 | 说明 |
---|---|
$args | 用于获取请求中的参数。例如:http://192.168.110.92/test?name=tom&age=18,$args的值就为name=tom&age=18 |
$host | 用于获取请求中的主机部分的值。例如:http://192.168.110.92/test?name=tom&age=18,$host的值为192.168.110.92。 |
$http_user_agent | 用于获取请求中的user-agent字段值 |
$remote_addr | 用于获取客户端的ip地址 |
$remote_port | 用于获取客户端与服务器建立连接的端口号 |
$request_method | 用于获取客户端的请求方式,例如get、post等 |
$request_uri | 用于获取当前请求的uri。例如:http://192.168.110.92/test?name=tom&age=18,$request_uri的值就为/test?name=tom&age=18 |
$query_string | 与$args作用相同 |
$scheme | 用于获取客户端请求使用的协议,例如http、https等 |
$server_addr | 用于获取服务端的ip地址 |
$server_name | 用于获取虚拟主机的名称 |
$server_port | 用于获取虚拟主机的监听的端口 |
$document_uri | 用于获取请求中的当前uri。例如:http://192.168.110.92/test?name=tom&age=18,$document_uri的值为/test |
$uri | 与$document_uri作用相同 |
$http_user_agent | 用于获取请求头中user-agent字段的值 |
$request_filename | 当前请求的文件路径 |
下面我们设置自定义变量,顺便体验使用一下这些全局变量。
location /test { default_type text/html; set $username zhangsan; return 200 <html>><p>username:$username</p><p>request_uri:$request_uri</p><p>document_uri:$document_uri</p><p>uri:$uri</p><p>query_string:$query_string</p><p>args:$args</p></html>; }
发起请求:http://192.168.110.98/test?name=tom
作用域:server, location
语法:if (condition) { … }
如果条件表达式为true,则执行该模块大括号中的指令。
tips:if
和(
之间有一个空格。
条件表达式有几种形式:
1)变量名,如果变量的值为空字符串或"0",则为false,其他条件为true。
if ($variable){ }
2)使用"=“和”!="比较变量和字符串是否相等,满足条件则为true,否则为false。
if ($request_method = post){ }
3)使用正则表达式与变量的值进行匹配。变量与正则表达式之间使用~
、~*
、!~
、!~*
,如果正则表达式包含}
或;
,则整个表达式应该用单引号或双引号括起来。
~
:表示匹配正则表达式,区分大小写~*
:表示匹配正则表达式,不区分大小写!~
:表示匹配正则表达式,区分大小写,并对匹配后的结果取反!~*
:表示匹配正则表达式,不区分大小写,并对匹配后的结果取反if ($http_user_agent ~ mozilla/5.0){ }
4)判断文件是否存在:-f
和!-f
if (-f $request_filename){ } if (!-f $request_filename){ }
示例:
location /test { default_type text/html; if (!-f $request_filename){ return 200 "<h1>file not exist</h1>"; } root html; }
我们已经在html目录下准备了一个test.html。
访问http://192.168.110.98/test.html,可以正常显示。
访问http://192.168.110.98/test,因为文件不存在,所以执行if条件块的指令。
5)判断目录是否存在:-d
和!-d
6)判断文件、目录或符号链接是否存在:-e
和!-e
7)判断文件是否可以执行:-x
和!-x
作用域:server, location, if;
语法:break;
在同一作用域中,中断该指令之后的其他指令,位于其前面的指令配置生效,位于其后面的指令配置则无效。
示例:如果url中存在参数,则执行if逻辑。
location /testbreak { default_type text/plain; set $username lisi; if ($args){ set $username wangwu; break; set $username zhaoliu; } add_header username $username; return 200 $username; }
访问http://192.168.110.98/testbreak,指令都正常执行。
访问http://192.168.110.98/testbreak?name=zhangsan,说明执行了if逻辑。
按break;
语句的定义来说,在其执行后,其作用域外后面的指令应该正常执行才对,但是这里直接返回了404。这个时候,就需要我们查看error.log
。
可以发现,错误提示为文件未找到,根据错误提示,我们需要在html目录下创建一个testbreak目录,然后在testbreak目录下创建一个index.html文件
cd /usr/local/nginx mkdir testbreak vim index.html <html> <body>this is testbreak</body> </html>
再次访问http://192.168.110.98/testbreak?name=zhangsan,可以看到break语句执行后,其作用域外后面的指令正常执行。
作用域:
server
, location
, if
语法:
return code [text];
return code url;
return url;
该指令可以停止处理并指定的响应码返回给前端。既可以返回文本,也可以重定向url。
示例:
location /testreturn { default_type text/plain; return 200 "test return"; }
location /testreturn { return 302 https://www.baidu.com; }
location /testreturn { return https://www.baidu.com; }
在了解set
、if
、break
、return
指令后,重头戏rewrite
指令登场。
作用域:server, location, if;
语法:rewrite regex replacement [flag];
regex
:用来匹配uri的正则表达式。replacement
:正则匹配成功后,用来替换uri的字符串。如果该字符串以http://
、https://
或$scheme
开头,则处理将停止,并重定向uri到客户端。flag
:是一个可选参数,其有4个候选值。flag值 | 说明 |
---|---|
last | 停止处理rewrite指令,并使用重写的uri去与各个location进行匹配 |
break | 停止处理rewrite指令,与break;效果一致 |
redirect | 如果replacement字符串不是以http://、https://或$scheme开头,则重定向到重写的uri,响应码为302 |
permanent | 重定向到重写的uri,响应码为301 |
rewrite指令通过正则表达式匹配uri,并修改uri。可同时存在多个rewrite指令,按照顺序依次对uri进行匹配和处理。
示例:
location /rewrite { rewrite ^/rewrite/aaa\w+$ https://www.baidu.com; rewrite ^/rewrite/(bbb)\w+$ /$1 last; rewrite ^/rewrite/(ccc)\w+$ /$1 break; rewrite ^/rewrite/(ddd)\w+$ /$1 redirect; rewrite ^/rewrite/(eee)\w+$ /$1 permanent; } location /bbb { default_type text/plain; return 200 "this is bbb"; } location /ccc { default_type text/plain; return 200 "this is ccc"; } location /ddd { default_type text/plain; return 200 "this is ddd"; } location /eee { default_type text/plain; return 200 "this is eee"; }
作用域:http, server, location, if
语法:rewrite_log on | off;
默认值:rewrite_log off;
该指令可以配置是否将ngx_http_rewrite_module
指令的处理结果以notice
级别的日志写入到error_log中。
示例:
location /rewrite { # 开启rewrite_log rewrite_log on; # 配置error_log error_log logs/error.log notice; rewrite ^/rewrite/aaa\w+$ https://www.baidu.com; rewrite ^/rewrite/(bbb)\w+$ /$1 last; rewrite ^/rewrite/(ccc)\w+$ /$1 break; rewrite ^/rewrite/(ddd)\w+$ /$1 redirect; rewrite ^/rewrite/(eee)\w+$ /$1 permanent; }
这样我们就可以在error.log中看到notice级别的日志。
在熟悉了ngx_http_rewrite_module
的相关指令后,我们来看看rewrite的相关使用场景。
**场景:**公司官网上线的时候地址为www.aaa.com,随着公司的不断发展,需要将官网地址升级为www.bbb.com,但是需要在访问www.aaa.com能够自动跳转到www.bbb.com。
tips:www.aaa.com和www.bbb.com需要指向同一ip。
**解决方案:**使用rewrite指令重写uri。
server { listen 80; server_name www.aaa.com; rewrite ^(.*) https://www.bbb.com$1; }
前面我们在【nginx静态资源防盗链】一文中已经简单的实现了静态资源的防盗链,但是展示在页面的是一个裂开的小图片,不够美观。
我们可以如下配置:
location ~^/.*\.(jpg|jpg|gif|jfif) { valid_referers www.example.com; if ($invalid_referer){ rewrite ^/ http://192.168.110.98/images/forbidden.jpg; } root html; }
如果出现盗链的情况,将会出现类似于如下效果:
以上就是nginx之rewrite实现url重写,nginx是多模块化的,还有很多高级功能,我们后面继续探索。
这些仅为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论