首页 > 技术文章 > nginx rewrite

Mr-Ding 2018-08-28 21:47 原文

一、什么是nginx rewrite?

nginx rewrite的主要功能是实现URL地址重写。

nginx的rewrite跪着需要PCRE软件的支持,即通过PCRE兼容正则表达式语法进行规则匹配。

二、nginx rewrite语法

rewrite指令语法:

指令语法:rewrite regex replacement [flag]:

默认值:none

应用位置:server、location、if

rewrite是实现URL重写的关键指令,根据regex(正则表达式)部分的内容,重定向到repla部分,结尾是flag标记,下面是一个简单的URL rewrite跳转的例子:

rewrite ^/(.*) http://www/dmtest1.com/$1 permanent;

 在上述指令中,rewrite为固定关键字,表示开启一天rewrite匹配规则,regex部分是^/(.*),这是一个正则表达式,表示匹配所有,匹配成功后跳转到http://www.dmtest1.com/$1。这里的$1失去前面(rege)部分括号里面的内容,结尾的permanent;是永久301重定向标记,即跳转到后面的http://www.dmtest1.com/$1地址上。

rege常用正则表达式说明:

\			将后面接着的字符标记为一个特殊字符或一个原义字符或一个向后引用。例如,"\n"匹配一个换行符,序列"\\"和"$"则匹配"$"
^			匹配输入字符串的起始位置,如果设置了RegExp对象的Multiline属性,^也匹配"\n"或"r"之后的位置
$			匹配输入字符串的结束位置,如果设置了RegExp对象的Multiline属性,$也匹配"\n"或"\r"之前的位置
*			匹配前面的字符零次或多次,例如,ol*能匹配"o"及"olll",*等价于{0,}
+			匹配前面的字符一次或多次,例如,"ol+"能匹配"ol"及"oll",但不能匹配"o",.+等价于{1,}
?			匹配前面的字符零次或一次,例如,"do(es)?"可以匹配"do"或"does"中的"do",.?等价于{0,1}
			当该字符紧跟在任何一个其他限制符(*,+?,{n},{n},{n,m})的后面时,匹配模式是非贪婪模式的,非贪婪模式会尽可能少地匹配所搜索的字符串,而默认的贪婪模式则会尽可能多地匹配所搜索的字符串,例如,对于字符串"oooo","o+?"将匹配单个"o",而"o+"将匹配所有"o"
.			匹配除"\n"之外的任何单个字符,要匹配包括"\n"在内的任何字符,请使用像"[.\n]"这样的模式
(pattern)	匹配括号内的pattern,并可以在后面获取对应的匹配,常用$0...$9属性获取小括号中的匹配内容。要匹配圆括号字符,请使用"\("或"\)"

 rewrite指令最后一项参数flag标记的说明:

last            本条规则匹配完成后继续向下匹配新的location URI规则
break        	本条规则完成即终止,不在匹配后面的任何规则
redirect		返回302临时重定向,浏览器地址栏会显示跳转后的URL地址
permanent		返回301永久重定向,浏览器地址会显示跳转后的URL地址

在以上的flag标记中,last和break用来实现URL重写,浏览器地址栏的URL地址不变,但在服务器端访问的程序及路径发生了变化。redirect和permanent用来实现URL跳转,浏览器地址栏会显示跳转后的URL地址。
last和break标记的实现功能类似,但二者之间有细微的差别,使用alias指令时必须用last标记,使用proxy pass指令时要使用break标记。last标记在本条 rewrite规则执行完毕后,会对其所在的server{…}标签重新发起请求,而break标记则会在本条规则匹配完成后,终止匹配,不再匹配后面的规则。

下面是可以用来判断的表达式:
-f和!-f用来判断是否存在文件
-d和!-d用来判断是否存在目录
-e和!-e用来判断是否存在文件或目录
-x和!-x用来判断文件是否可执行

下面是可以用作判断的全局变量:

参数 说明
$args 这个变量等于请求行中的参数。
$content_length 请求头中的Content-length字段。
$content_type 请求头中的Content-Type字段。
$document_root 当前请求在root指令中指定的值。
$host 请求主机头字段,否则为服务器名称。
$http_user_agent 客户端agent信息
$http_cookie 客户端cookie信息
$limit_rate 这个变量可以限制连接速率。
$request_body_file 客户端请求主体信息的临时文件名。
$request_method 客户端请求的动作,通常为GET或POST。
$remote_addr 客户端的IP地址。
$remote_port 客户端的端口。
$remote_user 已经经过Auth Basic Module验证的用户名。
$request_filename 当前请求的文件路径,由root或alias指令与URI请求生成。
$query_string 与$args相同。
$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相同。

 

三、nginx rewrite的企业应用场景

nginx的rewrite功能在企业里的应用非常广泛:

1、可以调整用户浏览的URL,使其看起来更规范,合乎开发及产品人员的需求。

2、为了让搜索引擎收藏网站内容,并让用户体验更好,企业会将动态URL地址伪装成静态地址提供服务。

3、网站换新域名后,让旧域名的访问跳转到新域名上,例如让京东的360buy换成jd.com.

4、根据特殊变量、目录客户端的信息进行URL跳转等。

四、nginx rewrite 301域名跳转

之前我们是通过别名的方式实现dmtest.com和www.dmtest.com访问同一个地址的,事实上,除了这个方式以外,还可以使用nginx rewrite 301跳转的方式来实现,配置如下:

[root@nginx extra]# cat dmtest.conf 
    server {
        listen       80;
        server_name   dmtest.com;
		rewrite ^/(.*) http://www.dmtest.com/$1 permanent;
		#当用户访问dmtest.com及下面的任意内容时,都会通过这条rewrite跳转到www.dmtest.com对应的地址;
	}
     server {
         listen       80;
         server_name   www.dmtest.com;
         location / {
             root   html/dmtest;
             index  index.html index.htm;
         }
         error_page   500 502 503 504  /50x.html;
         location = /50x.html {
             root   html;
         }
         access_log logs/access_dmtest.log main gzip buffer=32k flush=5s;
     }

 五、实现不同域名的跳转

 实现访问http://blog.dmtest.com时跳转到http://www.dmtest.com/blog/dm.html。

[root@nginx blog]# cat ../../../conf/extra/blog.conf 
    server {
        listen       80;
        server_name  blog.dmtest.com;
        location / {
            root   html/blog;
            index  index.html index.htm;
        }
		if ( $http_host ~* "^(.*)\.dmtest\.com$) {
			set $domain $1;
			rewrite ^(.*) http://www.dmtest.com/$domain/dm.html break;
			}
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

 跳转后,http://www.dmtest.com/blog/dm.html地址对应的站点配置如下:

[root@nginx blog]# cat ../../../conf/extra/dmtest.conf 
    server {
        listen       80;
        server_name  www.dmtest.com dmtest.com;
        location / {
            root   html/dmtest;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
		access_log logs/access_dmtest3.log main gzip buffer=32k flush=5s;
    }

 实现访问http://dmtest.com/bbs时跳转到http://bbs.dmtest.com。

在dmtest.conf下设置nginx rewrite规则如下:

[root@nginx extra]# cat dmtest.conf 
     server {
         listen       80;
         server_name   www.dmtest.com dmtest.com;
         location / {
             root   html/dmtest;
             index  index.html index.htm;
         }
         error_page   500 502 503 504  /50x.html;
         location = /50x.html {
             root   html;
         }
		rewrite ^(.*)/bbs/ http://bbs.dmtest.com break;
		access_log logs/access_dmtest.log main gzip buffer=32k flush=5s;
     }


[root@nginx extra]# cat bbs.conf 
    server {
        listen       80;
        server_name  bbs.dmtest.com;
        location / {
            root   html/bbs;
            index  index.html index.htm;
        }
    }

 有关rewrite特殊flag标记last与break的说明:

在跟location(即location/{......})中或server{......}标签中编写rewrite规则,建议使用last标记,而在普通的location(例如location/dm/{......}或if{})中编写rewrite规则,则建议使用break标记。

 

其他

四、Redirect语法
多目录转成参数
abc.domian.com/sort/2 => abc.domian.com/index.php?act=sort&name=abc&id=2

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

目录对换
/123456/xxxx -> /xxxx?id=123456

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

例如下面设定nginx在用户使用ie的使用重定向到/nginx-ie目录下:

  if ($http_user_agent ~ MSIE) {
   rewrite ^(.*)$ /nginx-ie/$1 break;
 }

目录自动加“/”

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

禁止htaccess

 location ~/\.ht {
  deny all;
 }

禁止多个目录

 location ~ ^/(cron|templates)/ {
   deny all;
   break;
 }

禁止以/data开头的文件
可以禁止/data/下多级目录下.log.txt等请求;

 location ~ ^/data {
  deny all;
 }

禁止单个目录
不能禁止.log.txt能请求

  location /searchword/cron/ {
   deny all;
  }

禁止单个文件

    location ~ /data/sql/data.sql {
             deny all;
     }

给favicon.ico和robots.txt设置过期时间;
这里为favicon.ico为99 天,robots.txt为7天并不记录404错误日志

  location ~(favicon.ico) {
   log_not_found off;
   expires 99d;
   break;
  }
    
  location ~(robots.txt) {
   log_not_found off;
   expires 7d;
   break;
  }

设定某个文件的过期时间
这里为600秒,并不记录访问日志

   location ^~ /html/scripts/loadhead_1.js {
     access_log   off;
     root /opt/lampp/htdocs/web;
     expires 600;
     break;
  }

文件反盗链并设置过期时间
这里的return 412 为自定义的http状态码,默认为403,方便找出正确的盗链的请求
“rewrite ^/ http://error.domain.com/error.gif;”显示一张防盗链图片
“access_log off;”不记录访问日志,减轻压力
“expires 3d”所有文件3天的浏览器缓存

 location ~* ^.+\.(jpg|jpeg|gif|png|swf|rar|zip|css|js)$ {
   valid_referers none blocked *.domain.com *.domain.net localhost 208.97.167.194;
    if ($invalid_referer) {
       rewrite ^/ http://error.domain.com/error.gif;
       return 412;
       break;
    }
    access_log   off;
    root /opt/lampp/htdocs/web;
    expires 3d;
    break;
 }

只充许固定ip访问网站,并加上密码

   root  /opt/htdocs/www;
   allow   222.11.11.11;
   allow   216.5.5.5;
   allow   105.10.10.10;
   deny    all;
   auth_basic "C1G_ADMIN";
   auth_basic_user_file htpasswd;

将多级目录下的文件转成一个文件,增强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;

将根目录下某个文件夹指向2级目录
如/shanghaijob/ 指向 /area/shanghai/
如果你将last改成permanent,那么浏览器地址栏显是 /location/shanghai/

 rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;

上面例子有个问题是访问/shanghai 时将不会匹配

    rewrite ^/([0-9a-z]+)job$ /area/$1/ last;
    rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;

这样/shanghai 也可以访问了,但页面中的相对链接无法使用,
如./list_1.html真实地址是/area /shanghia/list_1.html会变成/list_1.html,导至无法访问。
那我加上自动跳转也是不行咯

(-d $request_filename)它有个条件是必需为真实目录,而我的rewrite不是的,所以没有效果

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

知道原因后就好办了,让我手动跳转吧

   rewrite ^/([0-9a-z]+)job$ /$1job/ permanent;
   rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2 last;

文件和目录不存在的时候重定向:

    if (!-e $request_filename) {
       proxy_pass http://127.0.0.1;
    }

域名跳转

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

多域名转向

   server_name  www.domain.com www.domain.net;
                index index.html index.htm index.php;
                root  /opt/lampp/htdocs;

   if ($host ~ "domain\.net") {
     rewrite ^(.*) http://www.domain.com$1 permanent;
   }

三级域名跳转

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

域名镜像

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

 

推荐阅读