1. 最终目标
假设已有:
- 一个域名:
example.com - 一台拥有公网 IPv4 的 VPS:
203.0.113.10 - 主站应用监听:
127.0.0.1:18080 - 管理后台监听:
127.0.0.1:18081 - Nginx 已安装并监听公网
80/443
最终希望得到:
公网访问:
https://example.com
https://www.example.com
↓
Nginx
↓
127.0.0.1:18080
↓
主站
后台访问:
本地电脑
↓ SSH Tunnel
VPS 127.0.0.1:18081
↓
/admin
核心原则是:
主站公开,后台不公开。
2. 配置域名 DNS
在域名服务商的 DNS 控制台添加 A 记录:
记录类型 主机记录 记录值
A @ VPS 公网 IPv4
A www VPS 公网 IPv4
其中:
@表示根域名example.comwww表示www.example.com
例如:
A @ 203.0.113.10
A www 203.0.113.10
如果未来确实需要子域名,也可以添加:
A admin 203.0.113.10
但对于只通过 SSH 隧道访问的后台,没有必要保留 admin 的公网 DNS
记录。
3. 检查 DNS 是否生效
macOS / Linux 可以使用:
dig +short example.com
dig +short www.example.com
正常情况下应返回 VPS 公网 IP。
也可以直接查询域名的权威 DNS:
dig NS example.com +short
然后:
dig @<权威DNS服务器> example.com A +short
Clash / 代理环境下的特殊情况
如果本机使用 Clash、Clash Verge 等代理,并启用了 Fake-IP 模式,可能看到类似:
198.18.x.x
这通常不是网站真实的公网 IP,而是代理软件分配的 Fake-IP。
因此不要仅凭本机普通 dig 结果判断 DNS 配置错误。
可以:
- 查询权威 DNS;
- 暂时绕过代理;
- 或直接在 VPS 上安装
dnsutils后查询。
Ubuntu / Debian:
sudo apt update
sudo apt install dnsutils
4. 确认 VPS 服务监听状态
检查:
sudo ss -lntp | grep -E '18080|18081|:80|:443'
理想状态类似:
127.0.0.1:18080 gunicorn
127.0.0.1:18081 gunicorn
0.0.0.0:80 nginx
[::]:80 nginx
0.0.0.0:443 nginx
[::]:443 nginx
这里非常重要的一点是:
127.0.0.1:18080
127.0.0.1:18081
应用服务只绑定回环地址,而不是:
0.0.0.0:18080
0.0.0.0:18081
这样公网无法绕过 Nginx 直接访问 Gunicorn。
5. 配置 Nginx 主站
假设原本已经有:
server {
listen 80;
listen [::]:80;
server_name 203.0.113.10;
client_max_body_size 1m;
location ^~ /admin {
return 404;
}
location / {
proxy_pass http://127.0.0.1:18080;
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;
proxy_read_timeout 50s;
}
}
为了让域名访问主站,把:
server_name 203.0.113.10;
修改为:
server_name example.com www.example.com 203.0.113.10;
完整结构:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com 203.0.113.10;
client_max_body_size 1m;
location ^~ /admin {
return 404;
}
location / {
proxy_pass http://127.0.0.1:18080;
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;
proxy_read_timeout 50s;
}
}
其中:
location ^~ /admin {
return 404;
}
可以阻止通过公开主站访问后台路径:
https://example.com/admin
6. 检查并重载 Nginx
每次修改配置后,先测试,后重载:
sudo nginx -t
看到:
syntax is ok
test is successful
再执行:
sudo systemctl reload nginx
不要在配置尚未通过检查时直接重启 Nginx。
7. 测试 HTTP 主站
执行:
curl -I http://example.com
curl -I http://www.example.com
如果看到:
HTTP/1.1 200 OK
Server: nginx
说明:
DNS
↓
VPS
↓
Nginx
↓
Gunicorn :18080
整条 HTTP 链路已经打通。
8. 使用 Let's Encrypt 配置 HTTPS
安装 Certbot:
sudo apt install certbot python3-certbot-nginx
申请主站证书:
sudo certbot --nginx -d example.com -d www.example.com
配置完成后测试:
curl -I https://example.com
curl -I https://www.example.com
并检查 443:
sudo ss -lntp | grep ':443'
此时主站结构为:
Internet
↓
https://example.com
↓ :443
Nginx
↓
127.0.0.1:18080
↓
Gunicorn
9. 为什么不把管理后台直接暴露公网
管理后台虽然可以配置成:
admin.example.com
↓
Nginx
↓
127.0.0.1:18081
但如果后台本来就是按"SSH 隧道访问"设计的,应用通常还会主动检查请求来源。
例如公网访问时可能返回:
400
请通过 SSH 隧道使用本机地址访问后台。
这不是 Nginx 故障,而是应用自己的安全策略正在生效。
如果后台只供站长个人使用,没有必要为了一个漂亮的子域名拆掉这层保护。
10. 使用 SSH Tunnel 访问后台
假设后台监听:
127.0.0.1:18081
在本地电脑运行:
ssh -L 18081:127.0.0.1:18081 user@VPS_IP
保持 SSH 会话开启。
然后浏览器访问:
http://127.0.0.1:18081/admin
数据路径为:
本地浏览器
↓
127.0.0.1:18081
↓
SSH 加密隧道
↓
VPS
↓
127.0.0.1:18081
↓
管理后台
后台端口不需要对公网开放。
11. 如果曾经配置过 admin 子域名
如果之前已经建立:
admin.example.com
后来决定恢复为纯 SSH 隧道访问,应清理三处。
DNS
删除:
A admin VPS_IP
Nginx
删除所有:
server_name admin.example.com;
对应的 server { ... }。
修改后:
sudo nginx -t
sudo systemctl reload nginx
Let's Encrypt 证书
查看:
sudo certbot certificates
如果存在独立的 admin 证书:
sudo certbot delete --cert-name admin.example.com
12. Nginx 排错技巧
查看 Nginx 实际加载的配置
不要只看 /etc/nginx/sites-available/ 中的文件。
真正可靠的是:
sudo nginx -T
例如:
sudo nginx -T | grep -n -E 'server_name|proxy_pass|18080|18081'
它显示的是 Nginx 当前实际加载的完整配置。
查看启用的网站
ls -l /etc/nginx/sites-enabled/
sites-available 里的配置文件通常需要在 sites-enabled
中存在符号链接才会被加载。
绕过 DNS 测试 Host 匹配
HTTP:
curl -I -H 'Host: example.com' http://127.0.0.1
HTTPS:
curl -k -I \
--resolve example.com:443:127.0.0.1 \
https://example.com
这样可以把 DNS 问题和 Nginx 问题彻底分开。
13. systemd / Gunicorn 排错
如果不同端口返回了相同页面,先不要怀疑 Nginx。
检查服务:
systemctl cat your-public.service
systemctl cat your-admin.service
查看完整启动命令:
grep '^ExecStart=' /etc/systemd/system/your-public.service
grep '^ExecStart=' /etc/systemd/system/your-admin.service
理想情况下类似:
18080 → public_wsgi:app
18081 → admin_wsgi:app
直接访问两个 Gunicorn:
curl -I http://127.0.0.1:18080
curl -I http://127.0.0.1:18081
如果要确认返回页面:
curl -s http://127.0.0.1:18080 | head
curl -s http://127.0.0.1:18081 | head
这样可以判断问题究竟发生在:
DNS
Nginx
Gunicorn
Flask / Web 应用
哪一层。
14. 最终推荐架构
对于个人博客,推荐保持:
Internet
│
HTTPS :443
│
Nginx
│
127.0.0.1:18080
│
主站
后台则:
本地电脑
│
SSH Tunnel
│
VPS
│
127.0.0.1:18081
│
/admin
安全边界很清楚:
80/443:允许公网访问;18080:仅 VPS 本机;18081:仅 VPS 本机;- 后台:仅通过 SSH 隧道进入;
/admin:公开主站直接返回 404;- HTTPS:由 Nginx + Let's Encrypt 负责。
15. 一份部署检查清单
[ ] @ A 记录指向 VPS
[ ] www A 记录指向 VPS
[ ] DNS 权威解析正确
[ ] 主站只监听 127.0.0.1:18080
[ ] 后台只监听 127.0.0.1:18081
[ ] Nginx 监听 80
[ ] Nginx 监听 443
[ ] server_name 包含根域名和 www
[ ] nginx -t 通过
[ ] HTTP 主站正常
[ ] HTTPS 主站正常
[ ] HTTP 自动跳转 HTTPS
[ ] 公网 /admin 被阻止
[ ] admin 子域名未公开解析
[ ] 后台只能通过 SSH Tunnel 访问
[ ] Certbot 证书正常
[ ] 不再使用的证书已清理
结语
整个部署过程最重要的不是"让域名能打开网页",而是把每一层职责拆清楚:
DNS 负责找到服务器
Nginx 负责域名、HTTPS 与反向代理
Gunicorn 负责运行 Python Web 应用
SSH 负责保护私人管理入口
主站留在阳光里,后台藏在 SSH 隧道之后。结构简单,出了问题也容易沿着链路逐层定位。
还没有公开评论。