Nginx 文档明确警告称if 是邪恶的并且应尽可能避免,互联网上到处都有类似的警告。
但是,大多数警告都特别关注块if
中的不良行为location
。此外,Nginx 文档说:
在位置上下文中可以完成的唯一 100% 安全的事情是:
返回 ...;
重写...最后;
我的问题是:如果其中包含的唯一指令是,那么if
在块级别server
(而不是块)使用是否安全?例如,以下 www-to-non-www 重定向:location
return
server {
listen 80;
server_name example.com www.example.com;
if ($http_host != example.com) {
return 301 http://example.com$request_uri;
}
# more config
}
第二个问题:我知道做这种事情的推荐方法是使用两个server
具有不同值的块server_name
。如果这个是是可以接受的用途if
,还有理由继续使用两个独立的server
块吗?
答案1
nginx 收到请求时的第一步是评估 HTTPHost
标头/TLS SNI 字段,并根据评估结果选择虚拟主机。
这意味着Host
标题总是被处理。
现在,如果您指定if
重定向语句,则意味着Host
将检查两次标头,首先选择虚拟主机,然后检查条件if
。这对处理器来说是两倍的工作。
一个反驳可能是单独服务器块的内存消耗。然而,内存分配在 nginx 进程的整个生命周期中都是相同的,而Host
标头的双重评估发生在每个请求上。
答案2
摘自《如果是邪恶的》:
在某些情况下,也可以将 if 移动到服务器级别(这是安全的,因为其中只允许其他重写模块指令)。
这表明if
在一个server
块中比在一个块中更安全location
。
答案3
不确定你说的安全是什么意思,但让我讲一下我最近遇到的一个案例。我需要拒绝访问某个网站,除非传递了某个参数。假设配置如下所示:
server {
server_name localhost;
root /usr/share/nginx/html;
try_files $uri /index.html;
}
我添加了一个if
:
...
if ($arg_secret-key != 123456) {
return 404;
}
但nginx
即使我提供了秘密,也会给我 404:
$ curl -sS localhost/?secret-key=123456
事实证明,try_files
进行了内部重定向/index.html
(未传递参数),导致 404。因此,为了使其正常工作,需要这样做:
try_files $uri /index.html$is_args$args;
但实际上我只需要拒绝访问主页:
...
location = / {
if ($arg_secret-key != 123456) {
return 404;
}
}
这样,它就可以正常工作而无需更改try_files
。这是因为还有index
模块。并且try_files
不会被location
块继承。因此,index
模块会正确重定向到/index.html?secret-key=123456
,并且对于新请求,无需对 进行任何操作try_files
。
如果您不确定发生了什么,您可能需要运行nginx
内置程序--with-debug
并使用级别配置错误日志debug
,例如:
nginx.conf
:
error_log /var/log/nginx/error.log debug;
server {
server_name localhost;
root /usr/share/nginx/html;
...
}
$ docker run --rm --name nginx -p 1111:80 -dv $PWD/nginx.conf:/etc/nginx/conf.d/default.conf nginx:alpine nginx-debug -g 'daemon off;'
// launch `docker logs -f nginx` in a separate console, use Enter to separate requests
// processing starts with "generic phase: 0"
$ curl -sS localhost:1111/?secret-key=123456
// change nginx.conf
$ docker exec nginx nginx -s reload
$ curl -sS localhost:1111/?secret-key=123456
...
$ docker stop nginx
更多内容这里。
关于你的第二个问题,我认为在你的情况下server
最好使用 2 。因为这是开发人员建议的,如果你选择该if
版本,有一天你可能会在更改配置后遇到一些问题。或者你可能会意识到在某些情况下它不会像你预期的那样工作(在更复杂的情况下)。有人可能会说,if
在你可能不知道的配置中引入了复杂性。if
如果我对它的工作原理有很好的理解,或者没有其他选择,我会使用它。而且这个if
版本看起来性能可能更差。虽然这只是一个猜测,我无法提供任何数字。就我而言,我没有看到任何解决方法,因此这就是我现在要使用的。
答案4
你能可以,但强烈不建议。已经有官方关于这个的解释。
要运行多个站点,我建议为每个站点创建多个配置文件,然后在主文件中包括位置nginx.conf。