如何通过 Nginx 重写获取页码的干净 URL

如何通过 Nginx 重写获取页码的干净 URL

最近我们从 Apache 迁移到了 Nginx,在 Apache 下这曾经非常容易,只需将一些内容放在 .htaccess 上即可。

RewriteEngine on
RewriteBase /

# only rewrite if the requested file doesn't exist
RewriteCond %{REQUEST_FILENAME} !-s

# pass the rest of the request into index.php to handle
RewriteRule ^(.*)$ /index.php/$1 [L]

上述操作对于清理 URL 并让 index.php 处理所有请求非常有用。但在 Nginx 中,我们需要重写位置块中的每个唯一 URL。然而,这并不像 apache 那样“自动”。

我们的重写位置块的一些示例

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

location /p {
rewrite ^/p(?:/([a-z_]+))?$ /index.php?p=$1 last;
rewrite ^/p/all_articles/user/(.*)?$ /index.php?p=all_articles&user=$1 last;
try_files $uri $uri/ /index.php;
}

location /about_us {
rewrite ^/about_us /index.php?about_us last;
try_files $uri $uri/ /index.php;
}

location /search {
rewrite ^/search/(.*) /index.php?search=$1;
rewrite ^/search/(.*)/page/(.*)?$ /index.php?search=$1&page=$2 last;
try_files $uri $uri/ /index.php;
}

location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}

上面的代码在清理 URL 时做得很好,但是当我们需要获取页面时

/p/all_articles/用户/ABC/页/2

/index.php?p=all_articles&user=ABC&page=2

我们已经尝试过了

rewrite ^/p/all_articles/user/(.*)/pg(?:/([0-9]+))?$ /index.php?p=all_articles&user=$1&pg=$2 last;

这仅当我们放置在单独的位置块时才有效

location /page/all_articles {
rewrite ^/p/all_articles/user/(.*)/pg(?:/([0-9]+))?$ /index.php?p=all_articles&user=$1&pg=$2 last;
try_files $uri $uri/ /index.php;
}

并且当这样做时,它不会让

/p/all_articles/用户/ABC

加载。

此外,搜索结果页面根本不起作用。


我们遇到的另一个问题是文件夹 .htaccess

Order deny,allow
Deny from all
Options -Indexes

在 Apache 下,这将阻止除 php 脚本之外对该文件夹和文件的任何访问。我们尝试过,

location /(data|img)/ {
   deny all;
   return 404;
}

它确实会阻止对文件夹的访问,但是,如果您指定文件名,它仍然会提供服务,例如不会拒绝访问;

/data/backup_01012020.zip 在 apache .htaccess 下,只有某些用户在登录后才被允许访问。而在它之外,apache 将拒绝任何访问。但在 nginx 下,即使在尝试访问 /data/ 时也会给出 404。即使您未登录,它也会立即提供 backup_01012020.zip 文件。

现在我们不知道该做什么,以前使用 apache 很容易。我们的应用程序基于 PHP,index.php 能够处理所有干净的 URL 请求。如果 Nginx 只是将所有请求传递给索引并让它处理,而不是进行大量的重写和位置块,那就太好了。任何帮助都很好。

答案1

您可能对以下问题感兴趣重写标签,因为它包含了您的问题的许多变体。

您的 Apache 重写规则:

RewriteRule ^(.*)$ /index.php/$1 [L]

将整个请求 URI 附加到/index.php。在nginx小路URI(规范化)可在$uri变量。如果您也需要查询参数,则可以使用$请求uri反而。

因此,重写规则的严格翻译将是:

location / {
    # Size zero static files are served.
    # I don't believe that is an issue.
    try_files $uri /index.php$request_uri;
}
# If no other .php files are accessible a prefix location of '/index.php/'
# is safer.
location /index.php/ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    # Probably duplicates the contents of fastcgi-php.conf
    # fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    # include fastcgi_params;
}

deny对位置的指令/(data|img)/不起作用,因为您使用的是前缀匹配,而不是正则表达式匹配:

location ~ ^/(data|img)/ {
   # Only one is required
   deny all;
   # return 404;
}

答案2

重写的解决方案

location /search {
rewrite ^/search/(.*)/page/(.*)?$ /index.php?search=$1&page=$2 last;
rewrite ^/search/(.*) /index.php?search=$1 last;
try_files $uri $uri/ /index.php;
}

location /p/all_articles {
rewrite ^/p/all_articles/user/(.*)/page(?:/([0-9]+))?$ /index.php?p=all_articles&user=$1&page=$2 last;
rewrite ^/p/all_articles/user/(.*)?$ /index.php?p=all_articles&user=$1 last;
try_files $uri $uri/ /index.php;
}

注意,我所做的只是交换台词。致谢理查德·史密斯


谢谢皮奥特·P·卡瓦斯对于另一种解决方案,它可能有助于脚本 100% 兼容的人自行处理干净的 URL。

location / {
    # Size zero static files are served.
    # I don't believe that is an issue.
    try_files $uri /index.php$request_uri;
}
# If no other .php files are accessible a prefix location of '/index.php/'
# is safer.
location /index.php/ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    # Probably duplicates the contents of fastcgi-php.conf
    # fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    # include fastcgi_params;
}

只要您的脚本 100% 可以使用干净的 URL,上述解决方案就是可行的方法。在这里,您不需要放置 100 个重写位置块,nginx 会将整个请求 URI 附加到 /index.php,这非常有趣且有用,可能这是真正的解决方案,但就我而言,我的脚本与此并不 100% 兼容。但这仍然是一个很好的、聪明的解决方案。


防止文件夹、文件访问的解决方案

location ~ ^/(data|img)/ {
   # Only one is required
   deny all;
   # return 404;
}

致谢皮奥特·P·卡瓦斯指出,deny all被某些东西覆盖,在清理服务器块后,它确实解决了这个问题。另外,请确保使用或deny all;return 404;但不要一起使用。

相关内容