Safe/performant 使 nginx 重定向 /?p=123 样式链接的方法

Safe/performant way to make nginx redirect /?p=123 style links

我正在尝试将 nginx 301 重定向 wordpress 风格 foo.com/?p=123 风格的链接指向固定的 URL。

If is evil 和其他文档中,我了解到危险的 if 语句是基于参数重定向的唯一方法,我发现这样的配置似乎可以完成工作:

location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
}

location = / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;

    if ($arg_p = 4)  { return 301 /2012/12/hello-world; }
    if ($arg_p = 15) { return 301 /about;               }
    # ... a few hundred more lines like this
}

但是,我猜想使用这种配置 nginx 将不得不通过所有这些 if 语句来解决顶级 foo.com/ 请求,这可能并不理想。

什么是 "right" 硬编码像这样的大重定向列表,同时最小化 if 语句的 danger/cost 的方法?

第一个好主意是将重定向规则放在一个单独的文件中。这将使主要配置更具可读性。然后可以将包含规则的文件包含在 include 指令中。

另一个好主意是使用 map 而不是 if,因为它有更简洁的语法来定义长列表。

假设您决定将规则放入 /path/to/redirect/rules.conf。该文件的内容将如下所示:

# Each map defines the set of rules for one set of conditions.
# The blocks must be sorted in order of priority: every next
# map block has a higher priority than the previous one.
map $arg_p $p_condition {
    default "";

    4              /2012/12/hello-world;
    15             /about;

    # You can use regular expressions
    ~^1[6-9]$      /some/page;
}

# By combining the condition variables you can implement
# some sort of and/or/not logic in your rules
map "${arg_p}#${arg_q}" $pq_condition {

    # Note that it is necessary to inherit the result
    # of the previous map block to preserve them. And this
    # is where the priority of the blocks comes from
    default $p_condition;

    # The values of p _and_ q are 4 and 15
    4#15           /2012/12/hello-world;

    # The value of p is 5 _or_ the value of q is 16
    ~"^5#.*"       /another/page;
    ~"^.*?#16"     /another/page;

    # The value of p is _not_ 8 _and_ the value of q is 30
    ~"^[^8]#30"    /the/very/special/page;
}

# The final block is of the highest priority. It defines
# the variable, which value will be used as the URL for redirection
map $args $url_to_redirect_to {

    # Don't forget this important line
    default $pq_condition;

    # Here's another type of condition: check if the t variable
    # is present in the list of GET paramters
    ~^(&|\?)t=.*$  /yet/another/page;
}

现在剩下要做的就是在主配置中使用定义的规则:

# Note that map directives must be placed out of the `server` block
include /path/to/redirect/rules.conf;

server {
    ...

    location = / {
        ...

        if ($url_to_redirect_to != "") {
            rewrite ^ $url_to_redirect_to? permanent;
        }

        ...
    }

    ...
}

乍一看 map 块的级联可能看起来有些混乱,但是当您在其中放置大量规则时,您会发现这种方法的优势。