我见过很多新手卡在同一个问题上:服务明明启动了,systemctl status 显示 running,本机 curl 127.0.0.1:3000 也能返回页面,但浏览器访问 http://服务器IP:3000 就是打不开。
这类问题最烦人的地方是,它看起来像“服务器坏了”,其实大多数时候只是某一层没放行:程序只监听了本地地址、防火墙没开端口、云厂商安全组拦了、Nginx 没转发,或者端口被别的进程占了。
别一上来重装系统。按顺序查,十分钟通常能定位。
第一步不是开防火墙,也不是改 Nginx,而是确认问题在哪一层。
在 VPS 里执行:
systemctl status your-service
curl -I http://127.0.0.1:3000
curl -I http://服务器内网IP:3000
curl -I http://服务器公网IP:3000
如果 127.0.0.1 都访问不了,说明服务本身没正常工作,先看应用日志。
如果 127.0.0.1 能访问,但公网 IP 不行,基本就是监听地址、防火墙、安全组或网络层问题。
这个判断很重要。很多人服务本身有 bug,却一直改防火墙;也有人服务正常,却反复重启应用,方向完全反了。
这是最常见的坑。
很多开发框架默认只监听 localhost 或 127.0.0.1,本机能访问,外网肯定访问不了。
查监听地址:
ss -lntp
你会看到类似结果:
LISTEN 0 511 127.0.0.1:3000 0.0.0.0:* users:(("node",pid=1234))
如果是 127.0.0.1:3000,说明只允许本机访问。要让外网直连,需要改成监听:
0.0.0.0:3000
不同程序写法不一样:
# Vite / Node 常见写法
npm run dev -- --host 0.0.0.0
# Python 简单服务
python3 -m http.server 8000 --bind 0.0.0.0
# Uvicorn
uvicorn app:app --host 0.0.0.0 --port 8000
不过生产环境不一定建议直接暴露应用端口。更常见做法是应用监听 127.0.0.1,然后让 Nginx/Caddy 反向代理到外网 80/443。
服务监听没问题后,再查系统防火墙。
Ubuntu 常见是 UFW:
ufw status verbose
ufw allow 3000/tcp
CentOS / Rocky / AlmaLinux 常见是 firewalld:
firewall-cmd --list-ports
firewall-cmd --add-port=3000/tcp --permanent
firewall-cmd --reload
如果你直接用 iptables/nftables,就查:
iptables -L -n -v
nft list ruleset
这里有个提醒:不要为了省事直接关防火墙。临时排查可以短时间停一下,但长期裸奔很危险。正确做法是只放需要的端口,比如 80、443、22,或者某个明确的业务端口。
很多云服务器有两层防火墙:系统里面一层,控制台安全组一层。
你在 VPS 里 ufw allow 3000 了,但云厂商控制台没放 3000,外网一样访问不了。
常见平台都类似:
- AWS / Lightsail:Security Group / Firewall
- Google Cloud:VPC Firewall
- Azure:NSG
- Oracle Cloud:Security List + Network Security Group
- Vultr / DigitalOcean / Linode:Cloud Firewall
- 部分 VPS 商:控制面板自带防火墙
安全组里要确认:
- 入站规则是否允许 TCP 3000
- 来源 IP 是不是限制成了某个地址
- 规则绑定的是不是当前这台机器
- IPv4 和 IPv6 是否分别放行
- 云防火墙是否默认拒绝所有入站
我排查时经常先用一个临时测试端口,比如 8080,确认系统和云安全组两边都放行,再收敛到正式端口。
有时候服务启动失败,是因为端口已经被占用。
查端口占用:
ss -lntp | grep ':3000'
lsof -i :3000
如果发现端口被旧进程占着,先确认它是什么,不要直接 kill -9。可能是你之前启动过的同一个应用,也可能是另一个服务。
常见处理:
# 看进程详情
ps -fp 进程ID
# 正常停止 systemd 服务
systemctl stop your-service
# 确认后再杀进程
kill 进程ID
端口冲突时,应用日志里通常也会有 EADDRINUSE、address already in use 这类提示。
如果你不是直接访问应用端口,而是通过域名访问,那还要查反向代理。
Nginx 常见配置:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
}
检查配置和重载:
nginx -t
systemctl reload nginx
journalctl -u nginx -n 100 --no-pager
常见错误有:
proxy_pass端口写错- 应用监听的是 127.0.0.1:3000,Nginx 转发到 127.0.0.1:3001
- 域名 DNS 没解析到这台 VPS
- Nginx 配置改了但没 reload
- HTTPS 证书没配置好,浏览器看起来像网站打不开
Caddy 用户也类似,重点看 Caddyfile 里的 reverse_proxy 地址是否正确。
IPv6 现在越来越常见,也带来一些奇怪问题。
比如服务监听在:
[::1]:3000
或者只开了 IPv6,没有正确监听 IPv4。你从浏览器访问 IPv4,就会失败。
查监听:
ss -lntp
curl -4 http://服务器IP:3000
curl -6 http://[IPv6地址]:3000
如果你暂时不打算支持 IPv6,可以先把服务明确绑定到 0.0.0.0,安全组和防火墙也先按 IPv4 排查。等 IPv4 通了,再单独处理 IPv6。
不要 IPv4、IPv6、DNS、反代一起改。一次只改一层,才知道是哪一步解决的。
少数 VPS 商会屏蔽一些端口,比如 25、80、443、SMTP 相关端口,或者需要工单申请开放。
如果你确认:
- 程序监听正常
- 系统防火墙放行
- 安全组放行
- Nginx 配置正确
- 端口没有冲突
但外网仍然不通,可以换个端口测试:
python3 -m http.server 8080 --bind 0.0.0.0
然后从本地电脑访问:
curl -I http://服务器IP:8080
如果 8080 通,80/443 不通,可能是服务商、控制台防火墙或本机 Web 服务配置问题。这个时候带着测试结果去开工单,比只说“网站打不开”有效得多。
我自己的顺序是这样的:
- 本机
curl 127.0.0.1:端口 ss -lntp看监听地址- VPS 内
curl 公网IP:端口 - 系统防火墙放行端口
- 云厂商安全组放行端口
- 本地电脑
curl 公网IP:端口 - 查 Nginx/Caddy 反向代理
- 查 DNS、IPv6、HTTPS
- 换测试端口确认是否被商家限制
- 带证据找客服
这个顺序的好处是从内到外,一层层排除,不会乱改。
排查时最怕乱动:
- 一上来重装系统
- 直接关闭所有防火墙长期不管
- 同时改 Nginx、DNS、安全组和应用配置
- 不看日志,只凭感觉重启
- 端口没确认就申请退款
- 把 0.0.0.0 暴露给所有服务,尤其是数据库、Redis、管理面板
特别是数据库、Redis、Elasticsearch 这类服务,不要为了“外网能访问”直接开到公网。需要远程访问时,优先用 SSH 隧道、VPN、内网或白名单。
| 现象 | 优先检查 |
|---|---|
| 本机 curl 不通 | 应用日志、服务状态、端口占用 |
| 本机通,公网 IP 不通 | 监听地址、防火墙、安全组 |
| IP 能访问,域名不行 | DNS、Nginx、server_name |
| HTTP 通,HTTPS 不通 | 证书、443 端口、反向代理 |
| IPv4 通,IPv6 不通 | IPv6 监听、防火墙、AAAA 记录 |
| 某端口一直不通 | 商家端口限制、安全组、运营商拦截 |
VPS 外网访问不了,不要急着怀疑机器质量。大多数问题都是“服务只在本地听着”或者“某一层防火墙没放行”。按顺序查,别同时改十个地方,问题反而更容易解决。
