在我的 nginx 1.12.2
conf 文件中,我有:
upstream app {
server unix:/tmp/app.sock fail_timeout=0;
}
server {
listen 443 deferred;
root /some/dir;
try_files $uri @app;
# If the request is for the naked domain, just serve the index bundle
# without going through Rails
#
location = / {
try_files /index.html =404;
}
# If the request if for the /api prefix, go directly to Rails.
# This location block is not strictly required, but it could be a handy
# customization hook for extra headers and settings.
#
location /api/ {
# Extra conf!
try_files @app;
}
# The location directory allows very creative configurations.
# http://nginx.org/en/docs/http/ngx_http_core_module.html#location
#
# This is just a named location to be used in try_files.
location @app {
proxy_pass_request_headers on;
proxy_set_header ...
proxy_pass http://app;
}
}
这并不正确,因为它只有一个参数:
location /api/ {
# Extra conf!
try_files @app;
}
...但它很好地传达了我想要实现的目标。我想我可以try_files
通过在最后一个参数之前添加一个不存在的文件来开始工作。
这是try_files
唯一的方法吗,还是还有其他更惯用的指令?
答案1
您的方案将不起作用。当nginx
确定最后一个location
块来处理请求时,它将使用范围内的“设置和标头”,这些设置和标头可能是从周围块继承的,但将不会包括来自兄弟块的任何“额外标题和设置”——无论采用何种过程来查找最终location
块。请参阅这个文件了解更多信息。
如果您有适用于多个位置的通用语句,则可以将它们卸载到单独的文件中,并在必要时包含它们。例如:
location / {
try_files $uri @app;
}
location /api/ {
# Extra conf!
include my/proxy/conf;
}
location @app {
include my/proxy/conf;
}
看这个文件了解更多信息。
答案2
我们的 Nginx 配置相当复杂,我们希望使用命名位置来让事情变得更加简单干燥。由于这个要求,我们必须做你想做的事情。
可悲的是:我们找不到任何直接的方法来做到这一点,但好消息是你可以使用一个try_files
不影响性能的指令来欺骗 Nginx,将第一个参数指向/dev/null
:
try_files /dev/null @the_named_location;
这绝对是一种解决方法,通常我会烧毁任何包含此 hack 的 pull request,并仔细向开发人员解释不遵守标准的可怕之处,等等……但在你被说服并接受这个可悲的现实后,它实际上会让你的配置看起来好多了。真的好多了!
我们认为这种“黑客行为”是“可以接受的”,因为事实证明,就整体代码质量而言,它带来了巨大的改进。
(但有时我的眼睛仍然会疼痛……)
答案3
几个第三方模块指令允许直接跳转到指定位置,例如ngx.exec
来自 lua-nginx-module 或echo_exec
来自 echo-nginx-module(两个模块都与OpenResty包)。但是使用“vanilla”nginx,唯一的方法是使用try_files
指令技巧。不幸的是,@VictorSchröder假设用作/dev/null
参数try_files
将消除由于系统stat
调用错误而导致的性能损失,在这种情况下/webroot/dev/null
将被stat
编辑的是文件。每个人都可以使用下列的指令。因此,它与常用的解决方案没有区别,例如
try_files /nonexistent @app;
但是,可以传递一个空字符串作为参数try_files
:
try_files "" @app;
这不会消除stat
系统调用。但是,在这种情况下,webroot 目录将被stat
执行并检查为文件系统上现有的物理文件,但它很可能不是(好吧,至少除非您将服务器或位置根指向这样的文件,但您为什么要做这么疯狂的事情?)根据我的假设,现代内核应该stat
比不存在的文件系统对象更有效地缓存系统调用结果。这只是一个假设,所以如果有人读到这篇文章,更了解内核在这种情况下的行为,我很乐意听取任何澄清。
我认为最后一部分需要澄清一下。为什么该try_files
指令检查 Web 根目录是物理文件而不是目录?这是因为参数""
不是以斜杠结尾的。以斜杠结尾的参数会将try_files
其测试为目录,而不以斜杠结尾的参数会将try_files
其测试为文件。也就是说,如果位置根目录指向现有目录,则try_files "/" ...
第一个检查将始终成功(并且显式或隐式index
指令将在下一个请求处理阶段执行索引文件搜索的工作,假设该位置具有静态内容处理程序),try_files "" ...
在这种情况下,第一个检查将始终失败。
服务器 webroot 也可能是一个不存在的目录,例如当 nginx 仅用于反向代理时。与许多其他 nginx 指令一样root
,如果该指令未从之前的配置级别继承,/html
则相对于 nginx 前缀具有默认值。该前缀是在构建时指定的,可以使用命令进行检查nginx -V
(请参阅--prefix=...
configure 参数)。例如,如果此前缀等于/etc/nginx
(常见情况),则默认服务器 webroot 将为,/etc/nginx/html
而对于大多数环境来说,这不太可能是现有目录。
try_files "" ...
但我还是更喜欢try_files /nonexistent ...
。如果nonexistent
文件以任何方式出现在 webroot 目录中(想想某种破坏性的黑客攻击),第一个将继续工作,而第二个将不会。
try_files "" ...
当使用指令指定 webroot 目录时,可能会有构造用例alias
,尤其是对于精确或正则表达式匹配的位置。但是我看不出有任何可能的用例将它放在使用root
一个的位置内,所以我认为 nginx 开发团队可能会考虑更新try_files
模块源代码完全消除stat
这种使用情况下的额外调用。这将使“无条件”位置跳转成为可能,而不会产生任何额外的性能损失,同时保持与任何现有配置的兼容性。