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 (磁盘缓存该跳转信息)

image-20220815150427065

临时重定向 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; 
}