我目前正在配置 nginx 对某些错误页面和其他“默认”媒体文件(例如 favicon.ico 和 robots.txt)的处理,并且在使某些错误页面按照我想要的方式工作时遇到了一个小问题。
基本上,我想做的是为该服务器的根目录下的服务器提供某些文件,例如 /var/www/someserver.com/robots.txt。如果该文件不存在,我希望 nginx 转到“默认”,即 /var/www/default/robots.txt。这是我(成功)配置的基本要点:
server {
...
root /var/www/someserver.com;
location ~* ^/(robots\.txt)$ {
error_page 404 = @default;
}
location @default {
root /var/www/default;
}
}
效果很好。
我正在尝试对错误页面执行相同的操作,但是却无法实现:
server {
...
root /var/www/someserver.com;
error_page 404 /404.html;
location ~* ^/(404\.html)$ {
error_page 404 = @default;
}
location @default {
root /var/www/default;
}
}
请注意,此方法“有效”的意思是,如果您访问 someserver.com/404.html,它将首先尝试加载 /var/www/someserver.com/404.html,如果未找到,则返回到 /var/www/default/404.html。但是,如果您访问 someserver.com/blahblah,则仅当在 /var/www/someserver.com/ 中设置了 404 页面时,它才会显示该页面。如果该文件不存在,则它不会返回到默认目录。
无论如何,您可能已经了解我想要完成的事情(这就是我包含第一个工作示例的原因)。
有任何想法吗?
编辑:
根据 Martin F 的回答,我最终得出了以下结论:
# Doesn't work when error page is returned on a POST request
server {
...
root /var/www/someserver.com;
error_page 404 = @notfound;
error_page 500 502 504 = @server_error;
error_page 503 = @maintenance;
location @notfound {
try_files /404.html /../default/404.html =404;
}
location @server_error {
try_files /500.html /../default/500.html =500;
}
location @maintenance {
try_files /503.html /../default/503.html =503;
}
}
这真是太棒了。上面的 error_pages 和 locations 的实际块位于 server_defaults.conf 文件中,该文件包含在每个虚拟主机中,这就是为什么我没有将路径硬编码到每个位置,而是对默认值使用相对路径。
编辑2:
这种方法存在一个问题。如果您向返回错误的 URL 发送 POST 请求,则 POST 请求方法将与 try_files 尝试一起发送。这(对我来说)会导致 405 Not Allowed 错误,因为 nginx 本质上是尝试向 /default/500.html 发送 POST 请求,而不是仅获取该页面。
编辑3:
我发布了一个可行的解决方案,它与我最初的想法更加接近。
答案1
我最终选择了更接近我最初想法的东西。我所缺少的关键原来是指令recursive_error_pages
。我真正需要做的就是将其“打开”,这样我最初的想法就奏效了。下面是我的 conf 的相关部分现在的样子:
server {
...
root /var/www/someserver.com/;
error_page 400 404 /404.html;
error_page 500 502 504 /500.html;
error_page 503 /503.html;
recursive_error_pages on;
location ~* ^/(404\.html|500\.html|503\.html)$ {
log_not_found off;
error_page 404 = @default;
}
location @default {
log_not_found on;
root /var/www/default;
}
}
我在这里添加了其他错误类型,这些错误类型不是我原始问题的一部分,因为这就是 Martin F 的方法给我带来的麻烦,而 Martin F 的方法在其他方面都很棒。该log_not_found
指令只是确保当在原始根目录中找不到错误页面时,我的日志中不会出现 404 错误。
答案2
try_files是此处要采用的方法。以下配置应该可以工作,但我尚未测试其语法错误。
server {
...
root /var/www;
location / {
try_files /someserver.com$uri /default$uri /someserver.com$uri/ /default$uri/ @notfound;
}
location @notfound {
try_files /someserver/404.html /default/404.html =404; # =404 should make it use the nginx-default 404 page.
}
}
答案3
不幸的是,我的答案晚了几年,但我认为它可能对未来的搜索者有所帮助。我安装的 Nginx 版本是 1.2.4,我创建了以下配置片段,
server {
server_name www.example.com
root /var/www/example
## Errors -> Locations
error_page 400 /400.html;
error_page 403 /403.html;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
## Locations -> Fallback
location = /400.html {
try_files /400.html @error;
internal;
}
location = /403.html {
try_files /403.html @error;
internal;
}
location = /404.html {
try_files /404.html @error;
internal;
}
location = /50x.html {
try_files /50x.html @error;
internal;
}
## Fallback Directory
location @error {
root /var/www/error;
}
}
答案4
我发现在任何包含 error_page 的位置块中包含“proxy_intercept_errors on;”至关重要。
如果没有此设置,则位置块将拦截从上游返回的错误代码。
http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors