我的博客

反向代理实战:统一管理多个 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 最核心的用法之一。配置好之后,后端服务的变化对用户完全透明,维护起来也轻松多了。