Nginx 30x 重定向
2014-10-23 12:16:45 阿炯

为何要使用301重定向

在网站建设中需要网页重定向的情况很多:如网页目录结构变动,网页重命名、网页的扩展名改变、网站域名改变等。如果不做重定向,用户的收藏和搜索引擎数据库中的旧地址只能让访客得到一个404错误信息页面,给用户带来不友好的体验。301重定向不仅能使页面实现自动跳转,对于搜索引擎来说,也可能可以传递PR值。

rewrite命令
nginx的rewrite相当于apache的rewrite rule(大多数情况下可以把原有apache的rewrite规则加上引号就可以直接使用),它可以用在server,location和if条件判断块中,命令格式如下:
rewrite 正则表达式 替换目标 flag标记

flag标记可以用以下几种格式:
last – 基本上都用这个Flag。
break – 中止Rewirte,不在继续匹配
redirect – 返回临时重定向的HTTP状态302
permanent – 返回永久重定向的HTTP状态301

例如下面这段设定nginx将某个目录下面的文件重定向到另一个目录,$2对应第二个括号(.*)中对应的字符串:
location /download/ {
 rewrite ^(/download/.*)/m/(.*)\..*$ $1/nginx-rewrite/$2.gz break;
}

nginx重定向的if条件判断
在server和location两种情况下可以使用nginx的if条件判断,条件可以为以下几种:

正则表达式

匹配判断

~  为区分大小写匹配; !~为区分大小写不匹配
~* 为不区分大小写匹配;!~为不区分大小写不匹配

例如下面设定nginx在用户使用ie的使用重定向到/nginx-ie目录下:
if ($http_user_agent ~ MSIE) {
 rewrite ^(.*)$ /nginx-ie/$1 break;
}

文件和目录判断
-f和!-f判断是否存在文件
-d和!-d判断是否存在目录
-e和!-e判断是否存在文件或目录
-x和!-x判断文件是否可执行

例如下面设定nginx在文件和目录不存在的时候重定向:
if (!-e $request_filename) {
 proxy_pass http://127.0.0.1/;
}

return
返回http代码,例如设置nginx防盗链:
location ~* \.(gif|jpg|png|swf|flv)$ {
 valid_referers none blocked http://www.freeoa.net/ http://www.google.com/;
if ($invalid_referer) {
 return 404;
}
}

set
设置nginx变量

301重定向方法
进行了301重定向,把www.freeoa.net和freeoa.net合并,并把之前的域名也一并合并,有两种实现方法。

第一种方法是判断nginx核心变量host:
server {
server_name www.freeoa.net freeoa.net;
if ($host != 'www.freeoa.net' ) {
 rewrite ^/(.*)$ http://www.freeoa.net/$1 permanent;
}
...
}

第二种方法:
server {
server_name freeoa.net;
 rewrite ^/(.*) http://www.freeoa.net/$1 permanent;
}

测试了第一种方法ok,这两种方法中,permanent是关键,详细说明见nginx重定向规则说明。

last – 基本上都用这个Flag
break – 中止Rewirte,不在继续匹配
redirect – 返回临时重定向的HTTP状态302
permanent – 返回永久重定向的HTTP状态301


重启之后测试一下是否设定成功。
hto@netapp:~$ curl --head freeoa.net

会输出:
HTTP/1.1 301 Moved Permanently
Server: nginx/0.7.65
Date: Fri, 03 Sep 2014 03:00:11 GMT
Content-Type: text/html
Content-Length: 185
Connection: keep-alive
Location: http://www.freeoa.net/


nginx做反向代理时出现302错误

现象描述:nginx在使用非80端口做反向代理时,浏览器访问发现返回302错误

出错原因:proxy.conf文件中定义的proxy_set_header Host $host;

意思是nginx接收到浏览器请求后修改请求头中的host信息,然后再把请求转发给后端真实服务节点,服务节点响应后把返回信息传送给nginx,而由于nginx是使用的非80端口做代理,后端服务节点却依然以为nginx是80端口,所以响应信息没有正确的返回给nginx的非80端口。

解决办法:修改为proxy_set_header Host $host:$server_port;即可,这样就把请求头中的host修改为nginx的非80端口了,后端服务节点就知道响应应该返回的正确nginx代理端口


Nginx对不同ip来源重定向不同的页面


使用allow,deny指令只能很简单的限制访问来源,不能实现较为精细的控制。建议使用下面的方法:

1、使用正则匹配来源地址
if ($remote_addr ~ "10\.206\.30\.[7|8]") {
    return 301 'http://www.freeoa.net/';
 }
 
 将来源ip为10.206.30.7x或8x请求重定向到其它地方。
 
 网段匹配
 ($remote_addr ~ "10\.206\.[2|3]0")
 
Ip/Mask 写法在Nginx启动时的报警
nginx: [warn] low address bits of 182.148.201.0/16 are meaningless

错误原因是因为子网划分错误,比如 92.67.220.249/28,每个子网有16个ip所以起始ip一定要是16的倍数这个子网应该写成这样92.67.220.240/28(即网络位),修改完毕后再reload nginx就不会报错了。

2、使用geo模块对多个网段进行匹配

需要安装geo模块,如果编译的话需要使用--with-http_geoip_module选项,系统可能也要安装对应的geo软件开发包。当对多个网段匹配时,上面的写法就不那么友好了,就需要使用geo模块(该模块需要安装),在http段定义
geo $bgt_nmg {
    default 0;
    218.6.169.95 1;
    182.148.201.0/28 1;
}

在server里使用
if ($bgt_foa) {
    return 301 'http://www.freeoa.net/';
}

-------------------------------
Good IP (yours): serve page as it is
Bad IP (not yours): redirect to /some-page

location / {
if ($remote_addr != 1.2.3.4) {
    rewrite ^ http://www.example.com/some-page;
}
# rest of bla bla
}
location /some-page {
    try_files index.html index.php; # or whatever
}

location / {
    error_page 403 = @badip
    allow 127.0.0.1;
    deny all;
}

location @badip {
    return 301 $scheme://example.com/some-page;
}

if ( $remote_addr = 1.2.3.4 ) {
proxy_pass http://www.yourwebsite.com/otherpage.htm;
}