使用Nginx map指令动态设置反向代理上游服务器
嘿,你的这个配置思路完全贴合DRY原则的方向,但目前的代码还缺了关键的反向代理逻辑,没法直接实现需求。我来一步步给你拆解:
一、当前配置能否实现动态反向代理?
答案是:还不能,但只需要补一行代码就能搞定
你已经通过map指令把不同的Host头映射到了对应的后端地址,但你的server块里没有添加反向代理的核心指令——proxy_pass。只要在server块里加上这一行:
proxy_pass http://$backend;
就能让Nginx根据Host头自动把请求转发到对应的后端服务器了。
二、优化空间整理
结合多域名反向代理的实战经验,这里有几个能让配置更健壮、更符合DRY原则的优化点:
1. 处理未匹配的Host头
当前的map没有默认规则,如果收到一个不在列表里的Host请求,$backend变量会是空值,导致Nginx报错。可以添加默认规则:
map $http_host $backend { baz.mydomain.com hostname1:8080; foo.mydomain.com 192.168.1.10:8081; bar.mydomain.com hostname2:1234; ham.mydomain.com hostname2:5678; default 127.0.0.1:8888; # 指向一个默认后端,或者直接返回404 # default ""; # 也可以设为空,配合下面的if指令返回404 }
如果想直接拒绝未匹配的请求,可以再加一段逻辑:
if ($backend = "") { return 404; }
2. 用$host替代$http_host
$http_host完全依赖客户端发送的Host头,存在被篡改的风险;而$host会优先取Nginx配置的server_name,如果没有匹配的server_name才会用Host头,更可靠:
map $host $backend { # 映射规则不变 }
3. 抽离通用SSL配置
如果你的SSL配置(比如ssl_protocols、ssl_ciphers、ssl_prefer_server_ciphers这些)是通用的,把它们放到单独的文件里(比如/etc/nginx/conf.d/ssl-common.conf),然后在server块里用include引入,避免重复:
# ssl-common.conf内容示例 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m;
然后在server块里引用:
server { listen 443 ssl http2; server_name .mydomain.com; ssl_certificate /usr/share/nginx/certs/mydomain.com.pem; ssl_certificate_key /usr/share/nginx/certs/mydomain.com.key; include /etc/nginx/conf.d/ssl-common.conf; # 引入通用SSL配置 proxy_pass http://$backend; # 其他proxy配置... }
4. 引入后端健康检查(可选但推荐)
如果你的Nginx支持ngx_http_upstream_module(大部分官方包都支持),可以把后端定义成upstream块,这样可以添加健康检查,避免把请求转发到已宕机的后端:
upstream baz_backend { server hostname1:8080; health_check interval=5s; # 每5秒检查一次后端健康 } upstream foo_backend { server 192.168.1.10:8081; health_check interval=5s; } map $host $backend { baz.mydomain.com baz_backend; foo.mydomain.com foo_backend; bar.mydomain.com hostname2:1234; ham.mydomain.com hostname2:5678; default 127.0.0.1:8888; }
然后proxy_pass依然用http://$backend即可,Nginx会自动处理健康检查的逻辑。
5. 添加HTTP到HTTPS的跳转
为了提升安全性,建议添加一个监听80端口的server块,把所有HTTP请求重定向到HTTPS:
server { listen 80; server_name .mydomain.com; return 301 https://$host$request_uri; }
6. 日志中记录后端信息
在server块里添加自定义日志格式,把$backend变量记录进去,方便后续排查问题:
log_format proxy_log '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$backend"'; access_log /var/log/nginx/reverse_proxy.log proxy_log;
三、完善后的完整配置示例
# 定义后端映射 map $host $backend { baz.mydomain.com baz_backend; foo.mydomain.com foo_backend; bar.mydomain.com hostname2:1234; ham.mydomain.com hostname2:5678; default 127.0.0.1:8888; } # 后端健康检查配置(可选) upstream baz_backend { server hostname1:8080; health_check interval=5s; } upstream foo_backend { server 192.168.1.10:8081; health_check interval=5s; } # HTTP跳转到HTTPS server { listen 80; server_name .mydomain.com; return 301 https://$host$request_uri; } # HTTPS反向代理主配置 server { listen 443 ssl http2; server_name .mydomain.com; ssl_certificate /usr/share/nginx/certs/mydomain.com.pem; ssl_certificate_key /usr/share/nginx/certs/mydomain.com.key; include /etc/nginx/conf.d/ssl-common.conf; # 反向代理核心配置 proxy_pass http://$backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 自定义日志 log_format proxy_log '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$backend"'; access_log /var/log/nginx/reverse_proxy.log proxy_log; }
内容的提问来源于stack exchange,提问作者Tal Bull

