我在将 .htaccess 文件转换为 nginx 时遇到了麻烦。我有 3 个 .htaccess 文件。第一个 .htaccess 文件位于文档根目录中,如下所示:
Options +FollowSymLinks
RewriteEngine On
RewriteRule ^img-(.*)\.html img.php?id=$1 [L]
RewriteRule ^slide-(.*)\.html slider.php?id=$1 [L]
RewriteRule ^page-(.*)\.html page.php?name=$1 [L]
RewriteRule ^contact\.html$ contact.php [QSA,L,NC]
第二个.htaccess 文件位于名为 upload 的文件夹中:
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^http://(.+\.)?foo\.com/ [NC]
RewriteCond %{HTTP_REFERER} !^$
RewriteRule .*\.(jpe?g|gif|bmp|png)$ nohotlink.gif [L]
<Files ~ "\.(php|sql|php3|php4|phtml|pl|py|jsp|asp|htm|shtml|sh|cgi)$">
order allow,deny
deny from all
</Files>
第三个也是最后一个 .htaccess 文件位于上传文件夹的“small”子目录中:
RewriteEngine Off
现在我在 /etc/nginx 中创建了一个名为 includes 的文件夹,并使用以下重写规则创建了 3 个单独的 .access 文件:
对于位于文档根目录中的第一个 .htaccess 文件,我在 /etc/nginx/includes 中创建了一个名为 root.access 的文件。在这个文件中,我有:
# nginx configuration
location /img {
rewrite ^/img-(.*)\.html /img.php?id=$1 break;
}
location /slide {
rewrite ^/slide-(.*)\.html /slider.php?id=$1 break;
}
location /page {
rewrite ^/page-(.*)\.html /page.php?name=$1 break;
}
location /contact {
rewrite ^/contact\.html$ /contact.php break;
}
对于位于“upload”文件夹中的第二个文件,我在 /etc/nginx/includes 中创建了一个名为 upload.access 的文件。该文件包含以下内容:
# nginx configuration
location /upload {
if ($http_referer !~ "^http://(.+\.)?foo\.com/"){
rewrite .*\.(jpe?g|gif|bmp|png)$ /nohotlink.gif break;
}
}
location ~ \.(php|sql|php3|php4|phtml|pl|py|jsp|asp|htm|shtml|sh|cgi)$ {
deny all;
}
对于第三个也是最后一个文件,我在 /etc/nginx/includes 中创建了一个名为 small.access 的文件。该文件的内容如下:
# nginx configuration
location /upload/small {
}
在服务器块配置文件中我有:
location / {
try_files $uri $uri/ /index.php;
include /etc/nginx/includes/root.access;
}
location /upload {
include /etc/nginx/includes/upload.access;
}
location /upload/small {
include /etc/nginx/includes/small.access;
}
现在使用此配置,当我尝试访问网站时,我会收到 403 错误。Nginx 错误日志报告:
[error] 18156#0: *7 access forbidden by rule, client: 111.**.**.**, server: foo.com, request: "POST /upload.php HTTP/1.1", host: "foo.com", referrer: "http://foo.com/"
现在在 apache 下一切都正常了。但我不明白为什么会出现 403 错误。我还担心我所说的重写规则,除了 403 错误之外,根本无法正常运行。有人能帮我解决这个问题吗?我看不出这里出了什么问题。
答案1
这是此部分配置的标准 nginx 行为:
location /upload {
if ($http_referer !~ "^http://(.+\.)?foo\.com/"){
rewrite .*\.(jpe?g|gif|bmp|png)$ /nohotlink.gif break;
}
location ~ \.(php|sql|php3|php4|phtml|pl|py|jsp|asp|htm|shtml|sh|cgi)$ {
deny all;
}
}
为什么?
让我解释一下位置是如何工作的:当 nginx 读取配置文件时,它会将位置块分为 3 种类型:
- 精确的位置块例如
location = /upload { }
- 前缀位置块例如
location /upload { }
- 位置块包含常用表达例如
location ~ /upload { }
一旦请求到达 nginx,位置选择过程如下:
- 如果找到与 URI 匹配的精确位置块,nginx 将停止搜索其他位置块并提供该请求。
- 如果没有,nginx 将搜索最长匹配的前缀位置块,并在进入下一步之前记住它。
- 然后 nginx 依次检查包含正则表达式的位置块。第一个匹配的位置块将用于处理请求。
- 如果上一步没有找到任何内容,则 nginx 将使用步骤 2 中的前缀位置块来处理请求。如果未找到任何内容,则可能会发生几种情况,但这与主题无关。
因此,在您的情况下,与扩展匹配的位置块.php
优先于与/upload
URI 部分匹配的位置块。
编辑:澄清解决方案,添加替代解决方案。
使用^~
你可以告诉 nginx 改变其步骤 2 的行为,以便它将立即使用匹配的前缀位置,绕过正则表达式位置查找。因此,你有两个解决方案:
解决方案 1:
上传.访问:
if ($http_referer !~ '^http://(.+\.)?foo\.com/') {
rewrite '.*\.(jpe?g|gif|bmp|png)$' '/upload/nohotlink.gif' break;
}
if ($uri ~ '\.(php|sql|php3|php4|phtml|pl|py|jsp|asp|htm|shtml|sh|cgi)$') {
return 403;
}
相同的服务器块
解决方案 2:
上传.访问:
if ($http_referer !~ '^http://(.+\.)?foo\.com/') {
rewrite '.*\.(jpe?g|gif|bmp|png)$' '/upload/nohotlink.gif' break;
}
location ~ \.(php|sql|php3|php4|phtml|pl|py|jsp|asp|htm|shtml|sh|cgi)$ {
deny all;
}
服务器块:
location ^~ /upload {
include /etc/nginx/includes/upload.access;
}
现在,如果你没有设置转发 php 文件处理的位置,你当前的配置将不会产生任何效果:请查看 nginxfastcgi 模块。然后,您将需要更改 root.access 文件中的重写规则,以便它们不会在当前位置上下文中解析(即创建一个唯一的后备位置并更改break
为last
告诉 nginx 在重写后再次运行位置选择过程)。