创建NGINX重写规则

在此博客文章中,我们讨论了如何创建NGINX重写规则(NGINX Plus和开源NGINX软件都可以使用相同的方法)。重写规则更改客户端请求中的部分或全部URL,通常出于以下两个目的之一:

  • 为了通知客户他们正在请求的资源现在位于其他位置。示例用例是网站的域名已更改,希望客户使用规范的URL格式(带有或不带有www前缀)以及想要捕获并更正域名的常见拼写错误。在return和rewrite指令都适用于这些目的。
  • 在NGINX和NGINX Plus中控制处理流程,例如在需要动态生成内容时将请求转发到应用服务器。该try_files指令通常用于此目的。

注意:要了解如何将Apache HTTP服务器重写规则转换为NGINX重写规则,请参阅我们的博客文章“ 将Apache重写规则转换为NGINX重写规则”。

我们假设您熟悉HTTP响应代码和正则表达式(NGINX和NGINX Plus使用Perl语法)。

比较return,rewrite和try_files指令

通用NGINX重写的两个指令是return和rewrite,并且该try_files指令是将请求定向到应用程序服务器的便捷方法。让我们回顾一下指令的作用以及它们之间的区别。

该return指令

该return指令是两个通用指令中的简单指令,因此,我们建议使用它而不是rewrite在可能的情况下使用(有关原因和时间的更多信息)。您将或括return在指定要重写的URL 的server或location上下文中,它定义了供客户端在以后的资源请求中使用的更正(重写)的URL。

这是一个非常简单的示例,可将客户端重定向到新域名:

server {
    listen 80;
    listen 443 ssl;
    server_name www.old-name.com;
    return 301 $scheme://$request_uri;
}

该listen指令的意思是server块适用于HTTP和HTTPS流量。该server_name指令与域名为www.old‑name.com的请求URL匹配。该return指令告诉NGINX停止处理请求,并立即将代码301 (Moved Permanently)和指定的重写URL发送给客户端。重写的URL使用两个NGINX变量来捕获和复制原始请求URL中的值:$scheme是协议(http或https),并且$request_uri是包含参数的完整URI。

对于系列中的代码,该参数定义新的(重写)URL。3xxurl

return (301 | 302 | 303 | 307) url;

对于其他代码,您可以选择定义一个文本字符串,该字符串出现在响应的正文中(HTTP代码的标准文本,例如Not Foundfor 404,仍包含在标头中)。文本可以包含NGINX变量。

return (1xx | 2xx | 4xx | 5xx) ["text"];

例如,当拒绝没有有效认证令牌的请求时,此指令可能是合适的:

return 401 "Access denied because token is expired or invalid";

您还可以使用几个语法快捷方式,例如,如果需要则省略代码302。请参阅该return指令的参考文档。

(在某些情况下,您可能想返回比在文本字符串中实现的响应更复杂或更细微的响应。使用该error_page指令,您可以为每个HTTP代码返回完整的自定义HTML页面,以及更改响应代码或执行重定向。)

因此,该return指令易于使用,并且适合重定向满足两个条件的情况:重写的URL适用于与server或location块匹配的每个请求,并且您可以使用标准NGINX变量构建重写的URL。

该rewrite指令

但是,如果您需要测试URL之间的更复杂的区别,捕获原始URL中没有相应NGINX变量的元素,或者更改或添加路径中的元素怎么办?rewrite在这种情况下,您可以使用指令。

与return指令一样,您可以将rewrite指令括在server或location上下文中,该上下文定义要重写的URL。否则,这两个指令比相似的指令有更多不同,并且rewrite正确使用该指令可能更复杂。它的语法很简单:

rewrite regex URL [flag];

但是第一个参数regex表示NGINX Plus和NGINX仅在与指定的正则表达式匹配(除了匹配serveror location指令)时重写URL 。附加测试意味着NGINX必须执行更多处理。

第二个区别是该rewrite指令只能返回代码301或302。要返回其他代码,您需要在return指令之后包含一个rewrite指令(请参见下面的示例)。

最后,该rewrite指令不一定像这样停止NGINX的请求处理,return也不一定向客户端发送重定向。除非你明确地表明(与标志或URL的语法)要NGINX来停止处理或发送重定向,它贯穿于整个配置寻找指令中定义的重写模块(break,if,return,rewrite,和set)并按顺序处理它们。如果重写的URL与重写模块中的后续指令匹配,则NGINX将对重写的URL执行指示的操作(通常再次重写它)。

这是使事情变得复杂的地方,您需要仔细计划如何订购指令才能获得所需的结果。例如,如果原始location块和其中的NGINX重写规则与重写的URL相匹配,则NGINX会陷入循环,将重写一遍又一遍地应用到内置限制(最多10次)中。要了解所有详细信息,请参阅重写模块的文档。如前所述,我们建议您尽可能使用该return指令。

这是一个使用rewrite指令的示例NGINX重写规则。它匹配以字符串/ download开头的URL,然后在路径的后面某个位置包含/ media /或/ audio /目录。它将/ mp3 /替换为这些元素,并添加适当的文件扩展名.mp3或.ra。在$1和$2变量捕获并没有改变路径元素。例如,/ download / cdn-west / media / file1变为/download/cdn-west/mp3/file1.mp3。如果文件名带有扩展名(例如.flv),则表达式会将其剥离,并以.mp3替换。。

server {
    # ...
    rewrite ^(/download/.*)/media/(\w+)\.?.*$ $1/mp3/$2.mp3 last;
    rewrite ^(/download/.*)/audio/(\w+)\.?.*$ $1/mp3/$2.ra  last;
    return  403;
    # ...
}

上面我们提到,您可以在rewrite指令中添加标志以控制处理流程。last示例中的标志是其中之一:它告诉NGINX跳过当前server或location块中的任何后续Rewrite-module指令,并开始搜索location与重写的URL相匹配的新标志。

return此示例中的最终指令意味着,如果URL与任一rewrite指令都不匹配,则代码403将返回给客户端。

该try_files指令

与return和rewrite指令一样,该try_files指令放置在server或location块中。作为参数,它包含一个或多个文件和目录的列表以及最终的URI:

try_files file ... uri;

NGINX按顺序检查文件和目录的存在(通过root和alias指令的设置构造每个文件的完整路径),并提供找到的第一个文件和目录。要指示目录,请在元素名称的末尾添加斜杠。如果文件或目录都不存在,NGINX将执行内部重定向,该重定向将由最终元素(uri)定义。

为了使try_files指令生效,您还需要定义一个location捕获内部重定向的块,如以下示例所示。最后一个元素可以是一个命名位置,由一个初始符号(@)表示。

该try_files指令通常使用$uri变量,该变量表示URL在域名之后的部分。

在以下示例中,如果客户端请求的文件不存在,NGINX将提供默认的GIF文件。当客户端请求(例如)http://www.domain.com/images/image1.gif时,NGINX首先在适用于该位置的or 指令指定的本地目录中查找image1.gif(片段)。如果image1.gif不存在,NGINX将查找image1.gif /,如果不存在,它将重定向到/images/default.gif。该值与第二个指令完全匹配,因此处理停止,NGINX将该文件提供并标记为要缓存30秒。rootaliaslocation

location /images/ {
    try_files $uri $uri/ /images/default.gif;
}

location = /images/default.gif {
    expires 30s;
}

示例–标准化域名

NGINX重写规则最常见的用途之一是捕获网站域名中已弃用或非标准的版本,并将其重定向到当前名称。有几个相关的用例。

从旧名称重定向到当前名称

该样品NGINX重写规则永久重定向从请求www.old-name.com和old-name.com到,使用两个NGINX变量来捕捉值从原始的请求URL - $scheme是原始协议(HTTP或https),并且$request_uri是完整的URI(域名之后),包括参数:

server {
    listen 80;
    listen 443 ssl;
    server_name www.old-name.com old-name.com;
    return 301 $scheme://$request_uri;
}

因为$request_uri捕获了域名后面的URL部分,所以如果新旧站点之间的页面一对一对应(例如, / about具有相同的页面),则此重写是合适的基本内容为www.old‑name.com/about)。如果您除了更改域名之外还对网站进行了重组,则可以通过省略$request_uri以下操作将所有请求重定向到首页可能更安全:

server {
    listen 80;
    listen 443 ssl;
    server_name www.old-name.com old-name.com;
    return 301 $scheme://;
}

有关在NGINX中重写URL的其他一些博客在rewrite这些用例中使用了伪指令,例如:

# NOT RECOMMENDED
rewrite ^ $scheme://$request_uri permanent;

这比等效return指令效率低,因为它要求NGINX处理一个正则表达式,尽管它是一个简单的表达式(插入符[  ^ ],它与完整的原始URL匹配)。相应的return指令也使人类读者更容易理解:return 301更清楚地表明NGINX 301比rewrite ... permanent符号更返回代码。

添加和删除www前缀

这些示例添加和删除www前缀:

# add 'www'
server {
    listen 80;
    listen 443 ssl;
    server_name domain.com;
    return 301 $scheme://www.domain.com$request_uri;
}

# remove 'www'
server {
    listen 80;
    listen 443 ssl;
    server_name www.domain.com;
    return 301 $scheme://domain.com$request_uri;
}

再次,return它优于rewrite随后的等效项。在rewrite需要解释正则表达式-  -并创建自定义变量(),其实就相当于内置变量。 ^(.*)$ $1$request_uri

# NOT RECOMMENDED
rewrite ^(.*)$ $scheme://www.domain.com$1 permanent;

将所有流量重定向到正确的域名

这是一种特殊情况,当请求URL不匹配server并被location阻止时(可能是因为域名拼写错误),会将传入流量重定向到网站的首页。它通过组合指令的default_server参数listen和下划线作为server_name指令的参数来工作。

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    return 301 $scheme://www.domain.com;
}

我们使用下划线作为参数,server_name以避免无意中匹配真实域名–可以安全地假定没有站点将下划线作为其域名。server但是,与配置中的其他任何块都不匹配的请求最终会在这里结束,并且default_server用于listen告诉NGINX对其使用该块的参数。通过$request_uri从重写的URL中省略变量,我们将所有请求重定向到主页,这是一个好主意,因为域名错误的请求特别有可能使用网站上不存在的URI。

示例–强制所有请求使用SSL / TLS

此server阻止迫使所有访问者使用到您站点的安全(SSL / TLS)连接。

server {
    listen 80;
    server_name www.domain.com;
    return 301 https://www.domain.com$request_uri;
}

有关NGINX重写规则的其他一些博客也使用了此用例的if测试和rewrite指令,例如:

# NOT RECOMMENDED
if ($scheme != "https") {
    rewrite ^ https://www.mydomain.com$uri permanent;
}

但是此方法需要额外的处理,因为NGINX必须同时评估if条件并处理rewrite指令中的正则表达式。

示例–为WordPress网站启用漂亮的永久链接

NGINX和NGINX Plus是使用WordPress的网站非常流行的应用程序交付平台。以下try_files指令告诉NGINX检查是否存在文件,$uri然后检查目录$uri/。如果文件或目录都不存在,则NGINX将重定向返回到/index.php并传递查询字符串参数,该$args参数将捕获该参数。

location / {
    try_files $uri $uri/ /index.php?$args;
}

示例–删除对不支持的文件扩展名的请求

由于各种原因,您的站点可能会收到请求URL,这些请求URL的文件扩展名与您未运行的应用程序服务器相对应。在这个来自Engine Yard博客的示例中,应用程序服务器是Ruby on Rails,因此无法处理由其他应用程序服务器(Active Server Pages,PHP,CGI等)处理的文件类型的请求,因此需要将其拒绝。在server将对动态生成的资产的任何请求传递给应用程序的块中,此location伪指令在非Rails文件类型到达Rails队列之前将其丢弃。

location ~ .(aspx|php|jsp|cgi)$ {
    return 410;
}

严格来说,响应代码 410 (Gone)用于以下情况:请求的资源以前在此URL上可用,但现在不再可用,并且服务器不知道其当前位置(如果有)。与响应代码相比,它的优势404在于它可以明确指示资源永久不可用,因此客户端将不会再次发送请求。

您可能希望通过返回响应代码403 (Forbidden)和诸如"Server handles only Ruby requests"文本字符串之类的解释为客户提供更准确的故障原因指示 。另外,该指令返回时也不会解释:deny all403

location ~ .(aspx|php|jsp|cgi)$ {
    deny all;
}

代码403隐式地确认所请求的资源存在,因此,404如果您想通过向客户端提供尽可能少的信息来实现“通过模糊性实现安全性”,则代码可能是更好的选择。缺点是客户端可能会重复重试该请求,因为404它不会指示失败是暂时的还是永久的。

示例–配置自定义路由

在来自MODXCloud的此示例中,您有一个资源,它充当一组URL的控制器。您的用户可以为资源使用更易读的名称,然后您重写(而非重定向)该资源,以由控制器在listing.html处进行处理。

rewrite ^/listings/(.*)$ /listing.html?listing=$1 last;

例如,将用户友好的URL http://mysite.com/listings/123重写为由listing.html控制器http://mysite.com/listing.html?listing=123处理的URL 。