Skip to content

Nginx Rewrite 地址重写

Rewrite规则可以实现对url的重写,以及重定向

应用场景

  • 地址跳转和协议跳转
  • 伪静态,利于搜索引擎爬虫和SEO
  • 调整用户浏览的URL,看起来规范,利于SEO
  • 网站更换新域名
  • 根据特殊的变量、目录、客户端信息进行跳转

规则优先级

  1. 先执行server块的rewrite指令
  2. 其次执行location匹配规则
  3. 最后执行location中的rewrite

全局变量

rewrite过程中,会用到一些全局变量:

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

配置语法

  • Syntax# rewrite regex replacement [flag];
  • Default:——
  • Context:server、location、if

regex:

  • ~ 为区分大小写匹配

  • ~* 为不区分大小写匹配

  • !~!~*分别为区分大小写不匹配及不区分大小写不匹配

Location优先级:

ocation 匹配方式有以下几种

  1. location = /path/a/exact.png {}: 精确匹配
  2. location ^~ /path/a/ {}: 优先前缀匹配(符合最长匹配原则)
  3. location ~ /Path?/ {} : 区分大小写正则匹配(首次匹配原则)
  4. location ~* /path?/ {}: 不区分大小写正则匹配(首次匹配原则)
  5. location /path/a/test.png {} : 前缀匹配(符合最长匹配原则)

location 优先级匹配与配置的先后顺序无关,优先级排列顺序如下

精确匹配 > 优先前缀匹配 > 区分大小写正则匹配=不区分大小写正则匹配 > 前缀匹配

文件及目录匹配:

  • -f!-f用来判断是否存在文件

  • -d!-d用来判断是否存在目录

  • -e!-e用来判断是否存在文件或目录

  • -x!-x用来判断文件是否可执行

Flag参数:

标记符号说明
last终止在本location块中处理接收到的URI,并将此处重写的URI作为新的URI使用其他location进行处理。(只是终止当前location的处理)【如果没有匹配到,会继续向下匹配】
break将此处重写的URI作为一个新的URI在当前location中继续执行,并不会将新的URI转向其他location。【如果没有匹配到,则不再向下匹配,直接返回结果404】
redirect将重写后的URI返回个客户端,状态码是302,表明临时重定向,主要用在replacement字符串不以“http://”,“ https://”或“ $scheme” 开头;【地址栏会显示跳转后的地址】
permanent将重写的URI返回客户端,状态码为301,指明是永久重定向;【地址栏会显示跳转后的地址】

breaklast区别举例:

break 只要匹配到规则,则会去本地配置路径的目录中寻找请求的文件; 而last只要匹配到规则,会对其所在的server(...)标签重新发起请求。

server {
        listen 80;
        server_name example.com;
        root /code;

        location ~ ^/break {
                rewrite ^/break /test/ break;
        }
        location ~ ^/last {
                rewrite ^/last /test/ last;
        }
        location /test/ {
                default_type application/json;
                return 200 "ok";
        }
}
server {
        listen 80;
        server_name example.com;
        root /code;

        location ~ ^/break {
                rewrite ^/break /test/ break;
        }
        location ~ ^/last {
                rewrite ^/last /test/ last;
        }
        location /test/ {
                default_type application/json;
                return 200 "ok";
        }
}

break请求:

1、首先:会去查找本地的/code/test/index.html;

2、如果找到了,则返回/code/test/index.html的内容;

3、如果没找到该目录则报错404,如果找到该目录没找到对应的文件则403

last请求:

1、首先:会去查找本地的/code/test/index.html;

2、如果找到了,则返回/code/test/index.html的内容;

3、如果没找到,会对当前server重新的发起一次请求。example.com/test/

4、如果有location匹配上,则直接返回该location的内容。

5、如果也没有location匹配,再返回404;

示例集合

在调试环境下,开启rewrite日志对规则的匹配以便观察:

/var/log/nginx/error.log notice;

http{
    ...
    rewrite_log on;
    ...
}
/var/log/nginx/error.log notice;

http{
    ...
    rewrite_log on;
    ...
}

示例一(常用301、302跳转):

# 修改配置文件vhost1.conf
server {
            listen 80;
            server_name   vhost1.com;
    # 301 永久跳转
            rewrite   ^/(.*)     http://www.vhost1.com/$1 permanent;
 }
    server {
        listen       80;
        server_name  www.vhost1.com;
    location / {
        root   /usr/share/nginx/html/vhost1;
        index  index.html index.htm;
    }
    location /aaa {
            # 302 临时跳转,用户访问/aaa/目录其实是访问服务器的bbb目录
    		# rewrite ^/aaa/(.*)$ /bbb/$1 redirect;
    }
    location /ccc {
        # 用户访问vhost1.com/ccc其实是访问agou-ops.top网站
        rewrite (.*) https://agou-ops.top redirect;
    }
    # 用户访问couese-11-22-33.html实际上真实访问的是/course/11/22/33/course_33.html
    location /course {
        # 灵活配法
        alias /course;
        rewrite ^/course-(.*)-(.*)-(.*).html$ /$1/$2/$3/course_$3.html redirect;
        # 固定配法
        # rewrite ^/course-(.*) /11/22/33/course_33.html redirect;
    }
       error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
 }
# 修改配置文件vhost1.conf
server {
            listen 80;
            server_name   vhost1.com;
    # 301 永久跳转
            rewrite   ^/(.*)     http://www.vhost1.com/$1 permanent;
 }
    server {
        listen       80;
        server_name  www.vhost1.com;
    location / {
        root   /usr/share/nginx/html/vhost1;
        index  index.html index.htm;
    }
    location /aaa {
            # 302 临时跳转,用户访问/aaa/目录其实是访问服务器的bbb目录
    		# rewrite ^/aaa/(.*)$ /bbb/$1 redirect;
    }
    location /ccc {
        # 用户访问vhost1.com/ccc其实是访问agou-ops.top网站
        rewrite (.*) https://agou-ops.top redirect;
    }
    # 用户访问couese-11-22-33.html实际上真实访问的是/course/11/22/33/course_33.html
    location /course {
        # 灵活配法
        alias /course;
        rewrite ^/course-(.*)-(.*)-(.*).html$ /$1/$2/$3/course_$3.html redirect;
        # 固定配法
        # rewrite ^/course-(.*) /11/22/33/course_33.html redirect;
    }
       error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
 }

示例二(域名新旧跳转):

server
{
     listen 80;
     server_name www.old.com;
     rewrite "^/(.*)$" http://www.new.com/$1;
}
server
{
     listen 80;
     server_name www.old.com;
     rewrite "^/(.*)$" http://www.new.com/$1;
}

示例三(跳转站外网页):

 server {
        listen       80;
        server_name  vhost2.com;
        location / {
           root   /usr/share/nginx/html/vhost2;
            index  index.html index.htm;
        }
        error_page 404 /404.html;
            location = /40x.html {
        }
        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    if ( $http_host ~* "^(.*)") {
        set $domain $1;
        rewrite ^(.*) http://www.baidu.com break;
    }
 }
 server {
        listen       80;
        server_name  vhost2.com;
        location / {
           root   /usr/share/nginx/html/vhost2;
            index  index.html index.htm;
        }
        error_page 404 /404.html;
            location = /40x.html {
        }
        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    if ( $http_host ~* "^(.*)") {
        set $domain $1;
        rewrite ^(.*) http://www.baidu.com break;
    }
 }

示例四(自动将http跳转到https):

# 方法一
server {
        listen 80;
        server_name www.vhost.com;
        rewrite ^(.*) https://$server_name$1 redirect;
    # 或者
        return 302 https://$server_name$request_uri;
}       

server { 
        listen 443;
        server_namewww.vhost.com;
        ssl on;
}
# 方法二
	location / {
           root /usr/share/nginx/html;
           index index.html;
           if ( $scheme = http ) {
               rewrite  ^/(.*)  https://www.xxx.com/$1 ;    
           }
       }
   # 指定网页跳转https
   location /login {
       root /usr/share/nginx/html;
       index index.html;
       if ( $scheme = http ) {
           rewrite / https://www.xxx.com/login permanent;
       }
       auth_basic "input password";
       auth_basic_user_file /apps/nginx/conf/.htpasswd;
   }
# 方法一
server {
        listen 80;
        server_name www.vhost.com;
        rewrite ^(.*) https://$server_name$1 redirect;
    # 或者
        return 302 https://$server_name$request_uri;
}       

server { 
        listen 443;
        server_namewww.vhost.com;
        ssl on;
}
# 方法二
	location / {
           root /usr/share/nginx/html;
           index index.html;
           if ( $scheme = http ) {
               rewrite  ^/(.*)  https://www.xxx.com/$1 ;    
           }
       }
   # 指定网页跳转https
   location /login {
       root /usr/share/nginx/html;
       index index.html;
       if ( $scheme = http ) {
           rewrite / https://www.xxx.com/login permanent;
       }
       auth_basic "input password";
       auth_basic_user_file /apps/nginx/conf/.htpasswd;
   }

示例五(判断资源是否存在):

location / {
       root  /usr/share/nginx/html;
       index index.html;
       if ( !-f $request_filename ) {
           rewrite (.*) http://www.xxx.com/index.html;
       }
   }
   # 资源不存在则跳往首页
location / {
       root  /usr/share/nginx/html;
       index index.html;
       if ( !-f $request_filename ) {
           rewrite (.*) http://www.xxx.com/index.html;
       }
   }
   # 资源不存在则跳往首页

示例六(根据用户浏览器跳转指定目录):

# 如果用户使用的是IE浏览器则跳转至nginx-ie目录中去
if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$  /nginx-ie/$1 break;
}
# 如果用户使用的是IE浏览器则跳转至nginx-ie目录中去
if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$  /nginx-ie/$1 break;
}

示例七(资源防盗链):

location /images {
       root /data/nginx/pc;
       valid_referers none blocked server_names *.example.com example.* www.example.org/galleries/
               ~\.google\.;			 #当访问/images下的资源时,只有指定的这些才可以访问
       if ( $invalid_referer ) {     #如果访问资源的网站不是从上面指定的搜索引擎跳转过来的,则拒绝访问,并且返回状态码403
           return 403;
           rewrite ^/ http://xxx.com/daolian.png;
       }
   }
# ---
location ~* .(gif|jpg|png|swf|flv)$ {
valid_referers none blocked server_names *.example.com example.* www.example.org/galleries/
               ~\.google\.;
if ($invalid_referer) {
return 403;
	}
}
location /images {
       root /data/nginx/pc;
       valid_referers none blocked server_names *.example.com example.* www.example.org/galleries/
               ~\.google\.;			 #当访问/images下的资源时,只有指定的这些才可以访问
       if ( $invalid_referer ) {     #如果访问资源的网站不是从上面指定的搜索引擎跳转过来的,则拒绝访问,并且返回状态码403
           return 403;
           rewrite ^/ http://xxx.com/daolian.png;
       }
   }
# ---
location ~* .(gif|jpg|png|swf|flv)$ {
valid_referers none blocked server_names *.example.com example.* www.example.org/galleries/
               ~\.google\.;
if ($invalid_referer) {
return 403;
	}
}

示例八(多目录转成参数):

# 需求如下所示
abc.domian.com/sort/2 => abc.domian.com/index.php?act=sort&name=abc&id=2

if ($host ~* (.*)\.domain\.com) {
    set $sub_name $1;
    rewrite ^/sort\/(\d+)\/?$ /index.php?act=sort&name=$sub_name&id=$1 last;
}
# 需求如下所示
abc.domian.com/sort/2 => abc.domian.com/index.php?act=sort&name=abc&id=2

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

示例九(目录对换):

# 需求如下所示
/123456/xxxx -> /xxxx?id=123456

rewrite ^/(\d+)/(.+)/ /$2?id=$1 last;
# 需求如下所示
/123456/xxxx -> /xxxx?id=123456

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

示例十(设置某个子目录跳转页面):

location ^~ /baidu {
    rewrite ^.+ http://baidu.com/ last;
    break;
}
location ^~ /baidu {
    rewrite ^.+ http://baidu.com/ last;
    break;
}

示例十一(域名镜像):

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

示例十二(三级域名跳转):

if ($http_host ~* ^(.*)\.t\.test\.com$”) {
    rewrite ^(.*) http://top.test.com$1;
    break;
}
if ($http_host ~* ^(.*)\.t\.test\.com$”) {
    rewrite ^(.*) http://top.test.com$1;
    break;
}

示例十三(维护站点):

server {
        server_name localhost;
        listen 80;
        location / {
                root /data/code/weihutongzhi;
                index index.html;
        }
    if ($request_uri !~ "^/index.html$") {
        rewrite ^(.*) http:/urlxxx/index.html permanent;
    }

}

server {

        listen          443;
        server_name     localhost;

        ssl_certificate      xxx.pem;
        ssl_certificate_key  xxx.key;
        location / {
                root /data/code/weihutongzhi;
                index index.html;
        }
    if ($request_uri !~ "^/index.html$") {
        rewrite ^(.*) https://urlxxx/index.html permanent;
    }
}
server {
        server_name localhost;
        listen 80;
        location / {
                root /data/code/weihutongzhi;
                index index.html;
        }
    if ($request_uri !~ "^/index.html$") {
        rewrite ^(.*) http:/urlxxx/index.html permanent;
    }

}

server {

        listen          443;
        server_name     localhost;

        ssl_certificate      xxx.pem;
        ssl_certificate_key  xxx.key;
        location / {
                root /data/code/weihutongzhi;
                index index.html;
        }
    if ($request_uri !~ "^/index.html$") {
        rewrite ^(.*) https://urlxxx/index.html permanent;
    }
}

参考链接