Nginx实现网址重定向
Nginx服务器利用 ngx_http_rewrite_module 模块解析和处理rewrite请求,此功能依靠 PCRE(perl compatible regular expression),因此编译之前要安装PCRE库,rewrite是nginx服务器的重要功能之一,用于实现URL的重写,URL的重写是非常有用的功能,比如它可以在我们改变网站结构之后,不需要客户端修改原来的书签,也无需其他网站修改我们的链接,就可以设置为访问,另外还可以在一定程度上提高网站的安全性。
ngx_http_rewrite_module 模块
官方文档: https://nginx.org/en/docs/http/ngx_http_rewrite_module.html
if 指令
官方文档:https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#if
if指令用于条件匹配判断,并根据条件判断结果选择不同的Nginx配置,可以配置在server或location块中进行配置,Nginx的if语法仅能使用if做单次判断,不支持使用if else或者if elif这样的多重判断,用法如下:
if (条件匹配) {
action
}
if 指令使用正则表达式对变量进行匹配,匹配成功时if指令认为条件为true,否则认为false,支持以下条件判断运算符:
= #比较变量和字符串是否相等,相等时if指令认为该条件为true,反之为false
!= #比较变量和字符串是否不相等,不相等时if指令认为条件为true,反之为false
~ #区分大小写字符,可以通过正则表达式匹配,满足匹配条件为真,不满足匹配条件为假
!~ #区分大小写字符,判断是否匹配,不满足匹配条件为真,满足匹配条件为假
~* #不区分大小写字符,可以通过正则表达式匹配,满足匹配条件为真,不满足匹配条件为假
!~* #不区分大小字符,判断是否匹配,满足匹配条件为假,不满足匹配条件为真
-f 和 !-f #判断请求的文件是否存在和是否不存在
-d 和 !-d #判断请求的目录是否存在和是否不存在
-x 和 !-x #判断文件是否可执行和是否不可执行
-e 和 !-e #判断请求的文件或目录是否存在和是否不存在(包括文件,目录,软链接)
#注意:
#如果$变量的值为空字符串或0,则if指令认为该条件为false,其他条件为true。
#nginx 1.0.1之前$变量的值如果以0开头的任意字符串会返回false
#示例:
location /main {
index index.html;
default_type text/html;
if ( $scheme = http ){
echo "if-----> $scheme";
}
if ( $scheme = https ){
echo "if ----> $scheme";
}
#if (-f $request_filename) {
# echo "$request_filename is exist";
#}
if (!-e $request_filename) {
echo "$request_filename is not exist";
#return 409;
}
}
set 指令
可以利用set指定定义变量,变量的值为字符串且支持字符串和nginx内置变量进行字符串拼接。
location /main {
root /data/nginx/html/pc;
index index.html;
default_type text/html;
set $name magedu;
echo $name;
set $my_port $server_port;
echo $my_port;
}
break 指令
用于中断当前相同作用域(location)中的其他Nginx指令,位于break指令后的其他指令不会执行。
location /main {
root /data/nginx/html/pc;
index index.html;
default_type text/html;
set $name magedu;
echo $name;
break; #location块中break后面的其他ngx_http_rewrite_module模块指令不会执行(如set指令)
set $my_port $server_port;
echo $my_port;
}
return 指令
return用于完成对请求的处理,并直接向客户端返回响应状态码,比如:可以指定重定向URL(对于特殊重定向状态码,301/302等) 或者是指定提示文本内容(对于特殊状态码403/500等),处于此指令后的所有配置都将不被执行,return可以在server、if 和 location块进行配置
语法格式:
return code; #返回给客户端指定的HTTP状态码
return code [text]; #返回给客户端的状态码及响应报文的实体内容,可以调用变量,其中text如果有空格,需要用单或双引号引起来
return code URL; #返回给客户端的URL地址
范例:
service error[root@centos7 nginx]# cat conf.d/pc.conf
server {
listen 80;
server_name pc.test.org;
location / {
root /app/nginx/html/pc;
default_type text/html;
index index.html;
if ($scheme = http) {
#return 666;
#return 666 "not allow http";
#return 301 http://www.baidu.com;
return 500 "service error";
echo "if ------> $scheme"; #return 后面的指令不再执行
}
}
}
##访问测试
[root@centos7 nginx]# curl pc.test.org
service error[root@centos7 nginx]#
rewrite_log 指令
设置是否开启记录ngx_http_rewrite_module 模块日志记录到 error_log日志文件当中,可以配置在http、server、location 或 if 中
注意:开启rewrite日志需要提前设置日志级别为notice
##开启错误日志
[root@centos7 nginx]# cat /apps/nginx/conf/nginx.conf |grep error_log
error_log logs/error.log notice;
#开启rewrite_log
[root@centos7 nginx]# cat /apps/nginx/conf.d/pc.conf
server {
listen 80;
server_name pc.test.org;
location / {
root /app/nginx/html/pc;
default_type text/html;
index index.html;
set $name test;
rewrite_log on;
break;
set $my_port $server_port; #由于只用了break指令,故set指令不会执行
echo $my_port; #echo指令将报错
}
}
#访问测试
[root@centos7 nginx]# curl pc.test.org
#查看日志记录
[root@centos7 nginx]# tail -1 /apps/nginx/logs/error.log
2022/08/15 19:40:03 [warn] 1277#0: *7 using uninitialized "my_port" variable, client: 172.16.16.88, server: pc.test.org, request: "GET / HTTP/1.1", host: "pc.test.org"
rewrite 指令
通过正则表达式的匹配来改变URI,可以同时存在一个或多个指令,按照顺序依次对URI进行匹配,rewrite主要是针对用户请求的URL或者是URI做具体处理
官方文档:https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite
rewrite可以配置在 server、location、if 块中
语法格式:
rewrite regex replacement [flag];
rewrite将用户请求的URI基于regex所描述的模式进行检查,匹配到时将其替换为表达式指定的新的URI
注意:如果在同一级配置块中存在多个rewrite规则,那么会自下而下逐个检查; 被某条件规则替换完成后,会重新进行新一轮的替换检查,隐含有循环机制, 但不超过10次; 如果超过,提示500响应码,[flag]所表示的标志位用于控制此循环机制
如果替换后的URL是以http://或https://开头,则替换结果会直接以重定向返回给客户端, 即永久重定向301
正则表达式
. #匹配除换行符以外的任意字符
\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 #匹配不是单词开头或结束的位置
[^x] #匹配除了x以外的任意字符
[^magedu] #匹配除了magedu 这几个字母以外的任意字符
rewrite flag 使用介绍
利用nginx的rewrite的指令,可以实现url的重新跳转,rewrtie有四种不同的flag,分别是redirect(临时
重定向302)、permanent(永久重定向301)、break和last。其中前两种是跳转型的flag,后两种是代理型
- 跳转型指由客户端浏览器重新对新地址进行请求
- 代理型是在WEB服务器内部实现跳转
rewrite 格式
Syntax: rewrite regex replacement [flag]; #通过正则表达式处理用户请求并返回替换后的数据包。
Default: —
Context: server, location, if
flag 说明
redirect;
#临时重定向,重写完成后以临时重定向方式直接返回重写后生成的新URL给客户端,由客户端重新发起请求;使用相对路径,或者http://或https://开头,状态码:302
permanent;
#重写完成后以永久重定向方式直接返回重写后生成的新URL给客户端,由客户端重新发起请求,状态码:301
break;
#重写完成后,停止对当前URL在当前location中后续的其它重写操作,而后直接跳转至重写规则配置块之后的其它配置;结束循环,建议在location中使用
#适用于一个URL一次重写
last;
#重写完成后,停止对当前URI在当前location中后续的其它重写操作,而后对新的URL启动新一轮重写检查,不建议在location中使用
#适用于一个URL多次重写,要注意避免出现超过十次以及URL重写后返回错误的给用户
rewrite 案例:域名重定向
重定向分为临时重定向(301)和永久重定向(302),临时重定向不会缓存域名解析记录(A记录),但是永久重定向会缓存。
示例:因业务需要,需将访问源域名pc.test.com 的请求重定向到mobile.test.com
##准备两个站点
[root@centos7 nginx]# curl pc.test.com
pc_page
[root@centos7 nginx]# curl mobile.test.com
mobile_page
##配置跳转规则
[root@centos7 nginx]# cat conf.d/pc.conf
server {
listen 80;
server_name pc.test.com;
location / {
root /apps/nginx/html/pc;
default_type text/html;
index index.html;
#rewrite / http://mobile.test.com; #跳转至mobile.test.com
}
}
#测试跳转
[root@centos7 nginx]# curl pc.test.com -L
mobile_page
永久重定向 301
域名永久型调整,即域名永远跳转至另外一个新的域名,之前的域名再也不使用,跳转记录可以缓存到
客户端浏览器
永久重定向会缓存DNS解析记录, 浏览器中有 from disk cache 信息
示例:访问www.360buy.com (京东早期域名)将永久跳转至 www.jd.com (磁盘缓存该跳转信息)
临时重定向 302
域名临时重定向,告诉浏览器域名不是固定重定向到当前目标域名,后期可能随时会更改,因此浏览器
不会缓存当前域名的解析记录,而浏览器会缓存永久重定向的DNS解析记录,这也是临时重定向与永久
重定向最大的本质区别。
rewrite 案例:break与last
break 案例
[root@centos7 conf.d]# cat pc.conf
server {
listen 80;
server_name pc.test.com;
location /break {
root html;
index index.html;
rewrite ^/break/(.*) /test/$1 break; #重写URL后,直接返回相应的请求结果
}
location /test {
return 666 "====end====";
}
}
[root@centos7 conf.d]# curl pc.test.com/break/
====end====[root@centos7 conf.d]#
#访问/break/** ,被重写为/test/**,但是重写后的路径并不存在,报错404
[root@centos7 conf.d]# curl pc.test.com/break/**
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0</center>
</body>
</html>
last 案例
[root@centos7 conf.d]# cat pc.conf
server {
listen 80;
server_name pc.test.com;
location /break {
root html;
index index.html;
rewrite ^/break/(.*) /test/$1 last; #重写请求URL后,重新发起请求匹配location
}
location /test {
return 666 "====end====";
}
}
[root@centos7 conf.d]# curl pc.test.com/break/
====end====[root@centos7 conf.d]#
#访问/break/**, 被重写为/test/** , 重新发起新的匹配请求,匹配到/test,返回相应结果
[root@centos7 conf.d]# curl pc.test.com/break/**
====end====[root@centos7 conf.d]#
rewrite 案例:自动跳转https
[root@centos7 conf.d]# cat pc.conf
server {
listen 80;
listen 443 ssl;
server_name pc.test.com;
ssl_certificate /apps/nginx/certs/www.nginx.com.pem;
ssl_certificate_key /apps/nginx/certs/www.nginx.com.key;
ssl_session_cache shared:sslcache:20m;
ssl_session_timeout 10m;
location / { #配置全站https
root html/pc;
index index.html;
if ($scheme = http) { #
rewrite / https://$host redirect;
}
}
location /login { #配置部分站点https加密,如登录界面
if ($scheme = http) {
rewrite / https://$host/login redirect;
}
}
}
#访问测试,成功
[root@centos7 conf.d]# curl -ikL pc.test.com
HTTP/1.1 302 Moved Temporarily
Server: nginx/1.18.0
Date: Mon, 15 Aug 2022 17:43:20 GMT
Content-Type: text/html
Content-Length: 145
Connection: keep-alive
Keep-Alive: timeout=60
Location: https://pc.test.com
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Mon, 15 Aug 2022 17:43:20 GMT
Content-Type: text/html
Content-Length: 8
Last-Modified: Mon, 15 Aug 2022 12:24:47 GMT
Connection: keep-alive
Keep-Alive: timeout=60
ETag: "62fa3b0f-8"
Accept-Ranges: bytes
pc_page
rewrite 案例: 重定向错误页面到主页
案例:当用户访问一个不存在的URL时,将页面重定向至站点首页
[root@centos7 conf.d]# cat pc.conf
server {
listen 80;
server_name pc.test.com;
location / {
root html/pc;
index index.html;
if ( !-e $request_filename ) { #配置跳转规则
rewrite .* http://$host;
}
}
}
#访问不存在的页面测试
[root@centos7 conf.d]# curl pc.test.com/** -L
pc_page
[root@centos7 conf.d]# curl pc.test.com/kk -L
pc_page
其他应用案例
#案例1:如果客户端浏览器包含MSIE,则rewrite客户端请求到/msie目录下
if ( $http_user_agent ~ MSIE){
rewrite ^(.*)$ /msie/$1 break;
}
#案例2: 更换目录访问方式, 目录转换为对象存储形式
#要求:
#/20200106/static ->/static?id=20200106
#/20200123/image ->/image?id=20200123
rewrite ^/(\d+)/(.+)/ /$2?id=$l last;
#案例3:多目录转换访问方式
#要求: www.magedu.com/images/20200106/1.jpg => www.magedu.com/index.do?name=images&dir=20200106=&file=1.jpg
#规则配置:
if ($host ~* (.*)\.magedu\.com) {
rewrite ^/(.*)/(\d+)/(.*)$ /index.do?name=$1&dir=$2&file=$3 last;
}