Rewrite是什么?

Nginx的rewrite是支持正则表达式的,在上一篇文章里简单有写了一点正则表达式的内容,Nginx在编译的时候,需要按装PCRE库,这个是先决条件。


Rewrite功能主要作用就是对URL的重写,即网站的跳转。比如说某个新闻网页已经从“西甲板块”转移到了“欧冠板块”,那么网页的网址肯定也会发生改变,如果还输入老的网址,肯定得到的结果就是404,这样对用户的体验很不好,觉得这个网站什么破玩意,网页昨天在今天就没。于是管理员就可以使用nginx实现跳转,来“润物细无声”的把用户引导到新的网页上。比如把已经拆除的网页,引导到“找失踪儿童”这种公益网页上,比如可以智能的识别用户的浏览器,当用户使用的是手机上网的时候,就把对应手机版的网页呈现给用户。


举个例子,如果用户使用的是苹果产品的浏览器,nginx想对其不同的浏览器提供不同的内容。那么在nginx.conf里就可以在server里加上这么一句:

1
2
3
if (http_user_agent ~ Safari){
     rewrite ^(.*)$  /Safari/ $1  break ;
}

可见nginx里的if语句与shell的if是不同的,nginx里的if没有then和else,而是“if A 那就B”的模式。

http_user_agent指的是用户的浏览器,Safari是苹果浏览器(MSIE是micosoft IE,Firefox就是火狐),中间的~表示区分大小写。nginx里非变量是没有“=”的,这一点要注意。

如果用户当前使用的浏览器是Safari,那么就要rewrite,^是开头,$是结尾,(.*)指的是任意内容,第二句话的意思是把任意内容都跳转到/Safari/目录下的第一个变量,这里的第一个变量其实就是用户输入的那个地址。

break是flag标志,一定要加上。


也就说,如果用户是在Safari浏览器下的地址栏里输入www.JQK123.com/index.html,浏览器会自动跳转到www.JQK123.com/Safari/index.html上。


再举一个例子,

1
2
3
if (! -f $request_filename){
     remrite ^ /img/ (.*)$  /site/ $host /images/ $1 last;
}

!代表“非”,-f同shell一样,是检查文件是否存在的,第一句话指的是if request_filename不存在的话。就启动rewrite,具体rewrite的方式是:只要是/img/这个文件里要求的文件不存在,那么就转到“/site/指定的IP地址/images/请求的网页”上,最后的last同样是flag标志。


什么是flag?

nginx里的flag主要有以下四种:

1)last:相当于Apache里的L标记,表示完成了rewrite,浏览器地址栏url地址不跳转;

2)break:本条匹配完成之后,终止匹配,不会再匹配后面的原则,浏览器地址栏url地址不跳转;

3)redirect:302临时重定向,浏览器地址栏会显示跳转后的url地址;

4)permanent:301永久重定向,浏览器地址栏会显示跳转后的url地址;


如果是直接跟在server或者location后面的编写rewrite规则,那么推荐使用last标记,如果不是,比如server是www.JQK123.com,location是/news/,那么就要使用break标记。


举个例子,如果“访问的是www.ioio.com/bbs/  则跳转到 www.ioio.com/data/,同时地址栏也显示跳转",这样只需要在server/location下写“rewrite ^/bbs/?$ /data/ permanent;"即可。百度贴吧里输入wow会直接跳转到“魔兽世界”吧就是这个道理。


再举个例子, www.abc.com 原有的域名商谈崩了,现在仅留下一个首页,那么所有来源地址是www.abc.com 的请求全部改成了www.test.net,显示跳转后的地址,现在要做如下更改:

1
2
3
4
5
6
7
8
9
server{
     listen 80;     #监听80端口
     servername www. test .com;      #新服务器是www.test.com
     index index.html;     #默认网页
     root  /usr/local/nginx/html     #指定root目录
     if  ($host = "www.abc.com" ){    
        rewrite ^/(.*)$ http: //www . test .net/$1 permanent;  #显示新地址,告诉用户,已经搬家了
}
}

补充---$host:in this order of precedence: host name from the request line, or host name from the “Host” request header field, or the server name matching a request

直白的翻译一下:host变量的值按照如下优先级获得:

  1. 请求行中的host.

  2. 请求头中的Host头部.

  3. 与一条请求匹配的server name.


要注意一点,如果server里设定了rewrite,但是下面的location对具体的域名又给出了具体的动作,那么会以location的动作优先。


rewrite的语法规则

rewrite是支持变量的,但是只有变量才能“=”,这个前面说过了,直接就是"变量 判断 某某“的模式,同时rewrite支持$1~$9的位置化参数。

~是区分大小写的匹配;

~*是不区分大小写的匹配;

!是取反,跟shell、python一样;

-f 是文件是否存在;

-d是目录是否存在;

-e是文件或者目录是否存在(一劳永逸);

-x是文件是否执行;


return指令

location ~ .*\.(sh|bash)?${

    return 403;

}

这句话指的是,如果访问当年server下的所有以sh or bash结尾的文件,都会返回一个403的页面。那个红色的\表示转义符,去掉了.的特殊意义。


我个人觉得return最牛掰的地方是用来测试,比如说想知道nginx下各个参数都是神马东西,就可使用return,比如下面这个例子:

然后去浏览器进行访问,


 各个参数是什么,一幕了然。


类似智能DNS的用法---set

有些网站比较国际化,里面提供了多种语言的支持,比如中文、英文、日文、葡萄牙语、西班牙语等等。而且这些网站都有一个智能DNS解析:如果服务器检测到用户使用的是大陆的IP地址,会自动显示简体中文的网页页面,如果服务器检测到用户使用的是台湾/香港的IP地址来登陆网页,就会自动显示繁体中文的界面,如果服务器检测到用户使用的是美国的IP地址,就会自动显示英文办的网页。这网站比较明显的就是www.nba.com,大陆用户登录直接就跳转到http://china.nba.com/?gr=www。


当然除此之外,还有一种情况,就是公司有两套网站,一个是专门给内部人看的样子,另一个是专门给外网用户看的样子。同样的原理,检测到访问网站的是ip地址是192.168开头或者是内网的地址,那么就会呈现内网办网页,如果不是那些ip地址就会呈现外网版网页,这样的情况如何实现呢?

1
2
3
4
5
6
7
8
9
if  ($host ~* ^(.*?)\aaa.\.com$){     #$host指的是来源的ip,不区分大小写的匹配的aaa.com后缀
     set  $var_tz  '1' ;     #那么给var_tz这个变量指定为1
}
if  ($host ~* ^192\.168\.1\.(.*?)$){     #道理同上,如果来源的ip是192.168.1开头的
      set  $var_tz  '1' ;     #变量也是1
}
if  ($var_tz !~ '1' ){         #当变量不是1的时候
     rewrite ^/(.*)$ http: //www1 .aaa.com rediect;     #就跳转到www1.aaa.com 
}