nginx 常见错误配置
错误配置集锦
proxy_set_header
和 add_header
不生效的问题
最近在写 nginx 配置时遇到个问题:如下配置中,server
块中定义的所有 proxy_set_header
属性都不会生效:
server{
server_name 192.168.31.123:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location / {
proxy_pass http://xxx;
}
# ^~ 开头对 URL 路径进行前缀匹配,并优先于正则匹配。
location ^~ /ws
{
proxy_set_header Connection "upgrade";
proxy_set_header Upgrade $http_upgrade;
proxy_pass http://xxx;
}
}
查看官方文档 proxy_set_header - nginx docs,发现它明确提到,只有在当前 block 中不存在 proxy_set_header
的情况下,才会继承上层的 proxy_set_header
配置。
上面的 location ^~ /ws
块包含了 proxy_set_header
,导致 server
块中的 proxy_set_header
失效!
因此正确的写法是,应该把外部的 proxy_set_header
配置拷贝到 location ^~ /ws
块中
这里还有更详细的论述:Set the HTTP headers with add_header
and proxy_*_header
directives properly
rewrite xxx xxx break
后面的 return 语句不生效
如下配置中,rewrite
后面的 return
语句不会生效,请求会返回 404
:
# ......
http {
# ......
server {
# ......
location /xxx/ {
rewrite ^/xxx/(.*)$ /yyy/$1 break;
return 200 "hhh";
}
}
...
}
查阅官方文档 ngx_http_rewrite_module 发现,
rewrite 的 last
和 break
两个都会停止处理当前上下文中 ngx_http_rewrite_module
模块的所有指令,而 return
指令就是由 ngx_http_rewrite_module
模块提供的,因此它被直接忽略了。
由于请求未得到处理,导致 nginx 直接返回了 404
.
proxy_pass 的 upstream DNS 缓存问题
Nginx 的 upstream 地址只会在启动时被解析一次,后续如果 upstream DNS 存在变更,就会导致错误。
参考文章:
解决方案:
- 方案一:每次 DNS 变更都 reload nginx,比如写个脚本监控 DNS 变更,有变更就跑下 nginx reload
- 方案二:通过 set+resolver 的方式动态更新 DNS
- 但是这个方案不适合 QPS 高的情况,因为它不支持 keepalive
location / {
resolver 8.8.8.8;
set $backend https://demo-app.com$uri$is_args$args;
proxy_pass $backend;
include proxy_params;
}
- 但是这个方案不适合 QPS 高的情况,因为它不支持 keepalive
- 方案三:使用第三方模块
带 path 的 proxy_pass 语句
通常我们的 proxy_pass
是只传一个 upstream 名称,或者一个不带 Path 的 URI,举例:
location /xxx {
# requests_uri 会被原封不动地传递给 upstream
proxy_pass http://xxx.example.com:8080;
}
但是有时候我们会看到带 Path 的 proxy_pass 配置:
location /xxx {
# requests_uri 会被替换掉 location 匹配的前缀后,再传递给 upstream.
# 比如请求 /xxx/yyy.html,实际上代理到 upstream 的地址会是 /yyy.html
proxy_pass http://xxx.example.com:8080/;
}
location /xxx {
# 比如请求 /xxx/yyy.html,实际上代理到 upstream 的地址会是 /aaayyy.html
proxy_pass http://xxx.example.com:8080/aaa;
}
location /xxx {
# 比如请求 /xxx/yyy.html,实际上代理到 upstream 的地址会是 /aaa/yyy.html
proxy_pass http://xxx.example.com:8080/aaa/;
}