反向代理实战:统一管理多个 Web 服务
随着服务器上跑的服务越来越多(博客、API、监控面板、文件分享……),每个服务占一个端口,记都记不住。于是决定用反向代理把一切都统一到 443 端口上。
场景分析
我的服务器上现在有这些服务:
- 博客(localhost:8080)
- API 服务(localhost:3000)
- 监控面板(localhost:9090)
- 文件分享(localhost:8888)
目标:通过不同的路径访问这些服务,都走 443 端口。
Nginx 配置
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/certs/fullchain.pem;
ssl_certificate_key /etc/ssl/certs/privkey.pem;
# 博客 — 根路径
location / {
proxy_pass http://127.0.0.1:8080;
}
# API — /api/ 路径
location /api/ {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 监控面板 — /monitor/
location /monitor/ {
proxy_pass http://127.0.0.1:9090/;
}
# WebSocket 支持
location /ws/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
关键细节
proxy_pass 后面的斜杠——如果 destination 后面有斜杠(http://xxx:3000/),匹配到的路径前缀会被去掉再转发,否则会整个路径传过去。这个区别坑了我很久。
WebSocket——有些服务(如 Jupyter、VSCode Server)需要 WebSocket,不加 Upgrade 头会连不上。
路径冲突——两个 location 不能匹配同一个路径,否则优先规则可能会让你意想不到。
用子域名代替路径
路径方式有个问题:某些前端应用假设自己在根路径,放到子路径下会报错。这时用子域名更好:
server {
listen 443 ssl http2;
server_name blog.example.com;
location / { proxy_pass http://127.0.0.1:8080; }
}
server {
listen 443 ssl http2;
server_name api.example.com;
location / { proxy_pass http://127.0.0.1:3000; }
}
ssl_reject_handshake 技巧
如果不想暴露"这个 IP 有多个域名",可以加一个默认 server:
server {
listen 443 ssl default_server;
ssl_reject_handshake on;
}
这样直接访问 IP 的请求会被 TLS 层直接拒绝,不会泄露任何证书信息。
反向代理是 Nginx 最核心的用法之一。配置好之后,后端服务的变化对用户完全透明,维护起来也轻松多了。