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

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

比较return,rewrite和try_files指令

通用 NGINX 重写的两个指令是returnand rewrite,该try_files指令是将请求定向到应用程序服务器的便捷方式。让回顾一下指令的作用以及它们的不同之处。

该return指令

该return指令是两个通用指令中较简单的一个,因此建议使用它而不是rewrite尽可能使用它(稍后会详细介绍为什么和何时)。您将或包含return在指定要重写的 URL 的上下文中,它定义了更正(重写)的 URL,供客户端在将来的资源请求中使用。serverlocation

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

server {
    listen 80;
    listen 443 ssl;
    server_name www.old-name.com;
    return 301 $scheme://www.new-name.com$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;

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

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

该rewrite指令

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

与return指令一样,您将rewrite指令包含在定义要重写的 URL的serverorlocation上下文中。否则,这两个指令的不同之处多于相似,并且rewrite指令的正确使用可能会更复杂。它的语法很简单:

rewrite regex URL [flag];

但是第一个参数 ,regex意味着 NGINX Plus 和 NGINX 只有在匹配指定的正则表达式时才会重写 URL(除了匹配serverorlocation指令)。额外的测试意味着 NGINX 必须做更多的处理。

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

最后,该rewrite指令不一定会停止 NGINX 对请求的处理,return也不一定会向客户端发送重定向。除非你明确地表明(与标志或URL的语法)要NGINX来停止处理或发送重定向,它贯穿于整个配置寻找指令中定义的重写模块(break,if,return,rewrite,和set)并按顺序处理它们。如果重写的 URL 匹配来自 Rewrite 模块的后续指令,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指令

与returnandrewrite指令一样,try_files指令被放置在一个serverorlocation块中。作为参数,它需要一个或多个文件和目录的列表以及一个最终的 URI:

try_files file ... uri;

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

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

该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到www.new-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://www.new-name.com$request_uri;
}

因为$request_uri捕获了域名后面的 URL 部分,所以如果新旧站点之间存在一一对应的页面(例如,www.new-name.com/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://www.new-name.com;
}

其他一些关于在 NGINX 中重写 URL 的博客在rewrite这些用例中使用该指令,如下所示:

# NOT RECOMMENDED
rewrite ^ $scheme://www.new-name.com$request_uri permanent;

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

添加和删​​除 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参数 tolisten告诉 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指令中的正则表达式。

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

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

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

由于各种原因,您的站点可能会收到以与您未运行的应用程序服务器相对应的文件扩展名结尾的请求 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控制器处理的 URL ,http://mysite.com/listing.html?listing=123。