Nginx

如何重寫 Nginx 以獲取頁碼的干淨 url

  • January 16, 2020

最近我們從 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 中,我們需要重寫 location 塊中的每個唯一 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/user/ABC/page/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 簡單地將所有請求傳遞給索引並讓它處理而不是大量的重寫和定位塊,那就太好了。任何幫助都會很棒。

重寫解決方案

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;
}

請注意,我所做的只是換行。致理查德·史密斯


感謝Piotr P. Karwasz,對於另一個解決方案,它可能會幫助那些腳本 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;
}

Piotr P. Karwasz的學分指出,deny all它被某些東西覆蓋,在乾淨的伺服器塊上它確實解決了問題。還要確保使用其中一個deny all;return 404;不一起使用。

您可能對帶有rewrite 標籤的問題感興趣,因為它包含您問題的許多變體。

您的 Apache 重寫規則:

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

將整個請求 URI 附加到/index.php. 在nginx中,URI的路徑(標準化)在$uri變數中可用。如果您也需要查詢參數,則可以改用$request_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;
}

引用自:https://serverfault.com/questions/998969