Nginx路径重写

Nginx路径重写

hash070 507 2022-06-14

本文转载于 http://www.baiyp.ren/Nginx%E8%B7%AF%E5%BE%84%E9%87%8D%E5%86%99.html

原作者:晓风残月

博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

Nginx路径重写

img

ReWrite介绍

rewrite功能就是使用nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向。

rewrite和location的功能有点相像,都能实现跳转,主要区别在于rewrite常用于同一域名内更改获取资源的路径,而location是对一类路径做控制访问和反向代理,可以proxy_pass到其他服务器。

Nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向。
rewrite只能放在server{},location{},if{}中,并且只能对域名后边的除去传递的参数外的字符串起作用。
Rewrite主要的功能就是实现URL的重写,Nginx的Rewrite规则采用Pcre,perl兼容正则表达式的语法规则匹配,如果需要Nginx的Rewrite功能,在编译Nginx之前,需要编译安装PCRE库。
通过Rewrite规则,可以实现规范的URL、根据变量来做URL转向及选择配置。

使用示例

比如想把/xxx/a.html重定向到/b/a.html去
只需在配置文件中添加如下片段

rewrite /archives/break.html /s/about.html redirect;

如果是想重定向到另外一个站点,则需添加如下规则(域名换成自己的)
如需永久重定向,将redirece改为permanent

    if ($host ~ '^hash070.top'){
    rewrite ^/(.+?).html$ http://www.hash070.top/archives/$1.html redirect;
    }

ReWrite模块

nginx通过ngx_http_rewrite_module模块支持url重写、支持if条件判断,但不支持else。另外该模块需要PCRE支持,应在编译nginx时指定PCRE支持。根据相关变量重定向和选择不同的配置,从一个location跳转到另一个location,不过这样的循环最多可以执行10次,超过后nginx将返回500错误。同时,重写模块包含set指令,来创建新的变量并设其值,这在有些情景下非常有用的,如记录条件标识、传递参数到其他location、记录做了什么等等。

rewrite 应用场景

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

  • 可以调整用户浏览的URL,看起来更规范,合乎开发及产品人员的需求。
  • 为了让搜索引擎搜录网站内容及用户体验更好,企业会将动态URL地址伪装成静态地址提供服务。
  • 网址换新域名后,让旧的访问跳转到新的域名上。例如,访问京东的360buy.com会跳转到jd.com
  • 根据特殊变量、目录、客户端的信息进行URL调整等

ReWrite相关指令

指令 默认值 使用范围 作用
break none if,server,location 完成当前的规则集,不再处理rewrite指令,需要和last加以区分
if ( condition ) none server,location 用于检测一个条件是否符合,符合则执行大括号内的语句。不支持嵌套,不支持多个条件&&或处理
return none server,if,location 用于结束规则的执行和返回状态码给客户端。状态码的值可以是:204,400,402,406,408,410,411,413,416以及500~504,另外非标准状态码444,表示以不发送任何的Header头来结束连接。
rewrite regex replacement flag server,location,if 该指令根据表达式来重定向URI,或者修改字符串。指令根据配置文件中的顺序来执行。注意重写表达式只对相对路径有效。该指令根据表达式来重定向URI,或者修改字符串。指令根据配置文件中的顺序来执行。注意重写表达式只对相对路径有效。
uninitialized_variable_warn on/off on http,server,location,if 该指令用于开启和关闭未初始化变量的警告信息,默认值为开启。
set variable value none 该指令用于定义一个变量,并且给变量进行赋值。变量的值可以是文本、一个变量或者变量和文本的联合,文本需要用引号引起来。

ReWrite的语法

COPYrewrite regex replacement [flag];
  • rewrite:固定格式
  • regex: 正则表达式
  • replacement:重写后的URL
  • flag: 重写状态标记

例子

COPYrewrite ^/(.*) http://www.etiantian.org/$1 permanent;

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

执行顺序

很多情况下rewrite也会写在location里,它们的执行顺序是:

  1. 执行server块的rewrite指令
  2. 执行location匹配
  3. 执行选定的location中的rewrite指令

如果其中某步URI被重写,则重新循环执行1-3,直到找到真实存在的文件;循环超过10次,则返回500 Internal Server Error错误。

flag标志位

lag标记符号 说明
last 本条规则匹配完成后,继续向下匹配新的location URL规则
break 本条规则匹配完成即终止,不再匹配后面的任何规则
redirect 返回302临时重定向,浏览器地址栏会显示条状后的URL地址
permanent 返回301永久重定向,浏览器地址栏会显示条状后的URL地址

因为301和302不能简单的只返回状态码,还必须有重定向的URL,这就是return指令无法返回301,302的原因了。这里 last 和 break 区别有点难以理解:

  • last一般写在server和if中,而break一般使用在location中
  • last不终止重写后的url匹配,即新的url会再从server走一遍匹配流程,而break终止重写后的匹配
  • break和last都能组织继续执行后面的rewrite指令

常用正则

字符 描述
** 将后面接着的字符标记为一个特殊字符或一个原义字符或一个向后引用。如“\n”匹配一个换行符,而“”则匹配“”则匹配“
^ 匹配输入字符串的起始位置
$ 匹配输入字符串的结束位置
***** 匹配前面的字符零次或多次。如“ol*”能匹配“o”及“ol”、“oll”
+ 匹配前面的字符一次或多次。如“ol+”能匹配“ol”及“oll”、“oll”,但不能匹配“o”
? 匹配前面的字符零次或一次,例如“do(es)?”能匹配“do”或者“does”,”?”等效于”{0,1}”
. 匹配除“\n”之外的任何单个字符,若要匹配包括“\n”在内的任意字符,请使用诸如“[.\n]”之类的模式。
(pattern) 匹配括号内pattern并可以在后面获取对应的匹配,常用$0…$9属性获取小括号中的匹配内容,要匹配圆括号字符需要(Content)

if判断

简单重写很多时候满足不了需求,比如需要判断当文件不存在时、当路径包含xx时等条件,则需要用到if

语法

COPYif (表达式) {
   ... ...
}
注意
  • 当表达式只是一个变量时,如果值为空或任何以0开头的字符串都会当做false
  • 直接比较变量和内容时,使用=或!=
  • 正则表达式匹配,*不区分大小写的匹配,!~区分大小写的不匹配
内置条件判断
  • -f 和 !-f用来判断是否存在文件
  • -d 和 !-d用来判断是否存在目录
  • -e 和 !-e用来判断是否存在文件或目录
  • -x 和 !-x用来判断文件是否可执行

全局变量

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

nginx的配置文件中可以使用的内置变量以美元符$开始,也有人叫全局变量。其中,部分预定义的变量的值是可以改变的。

$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相同。

示例

简单示例

COPYserver {
    # 访问 /last.html 的时候,页面内容重写到 /index.html 中
    rewrite /last.html /index.html last;

    # 访问 /break.html 的时候,页面内容重写到 /index.html 中,并停止后续的匹配
    rewrite /break.html /index.html break;

    # 访问 /redirect.html 的时候,页面直接302定向到 /index.html中
    rewrite /redirect.html /index.html redirect;

    # 访问 /permanent.html 的时候,页面直接301定向到 /index.html中
    rewrite /permanent.html /index.html permanent;

    # 把 /html/*.html => /post/*.html ,301定向
    rewrite ^/html/(.+?).html$ /post/$1.html permanent;

    # 把 /search/key => /search.html?keyword=key
    rewrite ^/search\/([^\/]+?)(\/|$) /search.html?keyword=$1 permanent;
}

结合IF示例

COPYserver {
    # 如果文件不存在则返回400
    if (!-f $request_filename) {
        return 400;
    }
    
    # 如果host不是xuexb.com,则301到xuexb.com中
    if ( $host != "xuexb.com" ){
        rewrite ^/(.*)$ https://xuexb.com/$1 permanent;
    }
    
    # 如果请求类型不是POST则返回405
    if ($request_method = POST) {
        return 405;
    }
    
    # 如果参数中有 a=1 则301到指定域名
    if ($args ~ a=1) {
        rewrite ^ http://example.com/ permanent;
    }
}

结合location使用

COPYserver {
    
    listen 80;
    server_name lotus.com;
    
    location = /test.html {
        # 默认值为lotus
        set $name lotus;
    
        # 如果参数中有 name=xx 则使name用该值
        if ($args ~* name=(\w+?)(&|$)) {
            set $name $1;
        }
    
        # 301
        rewrite ^ /$name.html permanent;
    }
}

完整示例

COPYserver {
    listen 80;
    server_name lotus.com www.lotus.com;

    if ( $host != 'www.abc.com'  ) {
        rewrite ^/(.*) http://www.abc.com/$1 permanent;
    }

    location / {
        root /data/www/www;
        index index.html index.htm;
    }

    error_log    logs/error_www.abc.com.log error;
    access_log    logs/access_www.abc.com.log    main;
}