nginx 本地 200、外网超时——根在云安全组不在防火墙
在一台新开的云主机上装好 nginx,本地 curl localhost/ 返回 200,心想万事大吉——打开浏览器输入公网 IP,转圈、超时。换 curl 来试:
curl: (28) Connection timed out after 10001 milliseconds
或者有时候是:
curl: (52) Empty reply from server
两种报错看起来截然不同,结果我在 nginx 配置、iptables、firewalld、SELinux 上绕了一大圈,全部正常。
现象
四项诊断同时成立:
systemctl is-active nginx→activess -tlnp | grep :80显示监听在0.0.0.0:80,不是127.0.0.1- VM 内
curl http://localhost/返回 HTTP 200 tail -f /var/log/nginx/access.log只有::1或127.0.0.1的记录——外部请求从未出现在日志里
第 4 条是最关键的信号:如果外网请求连 access.log 都没进来,说明数据包根本没到达 VM 的网络栈,被上游拦掉了。
根因
云主机的网络访问控制分两层:
| 层 | 位置 | 工具 | |---|---|---| | 云安全组(Security Group) | 云平台 VPC 层,VM 外部 | 控制台 / CLI | | 本地防火墙 | VM 内部 | iptables / firewalld / ufw |
新建实例默认只开 SSH/22。HTTP/80 和 HTTPS/443 默认关闭,与本地 iptables 规则无关。iptables 全 ACCEPT 只代表 VM 内部不拦,拦截发生在更上游的 VPC 层,iptables 根本看不到这些包。
本地防火墙工具只管 VM 内部的包过滤。云平台安全组是独立的 SDN/VPC 层拦截,状态完全不同步。两个"防火墙"互相独立,都放行才能通。
curl (52) Empty reply 和 curl (28) Connection timed out 是同一个安全组规则缺失的两种表现——前者是 TCP 握手后 RST,后者是 SYN 被静默丢弃,取决于客户端路由路径,不必当成两个不同问题排查。
正解
第一步:确认是安全组问题
在 VM 上跑诊断四连:
# 确认监听 0.0.0.0
ss -tlnp | grep :80
# 确认本地 200
curl -sS -o /dev/null -w "%{http_code}\n" http://localhost/
# 确认 iptables 无 DROP
iptables -L INPUT -n --line-numbers | head
# 看日志有无外部 IP
tail -10 /var/log/nginx/access.log从另一台机器(或手机热点)打外网:
curl -sS --max-time 10 -o /dev/null \
-w "HTTP %{http_code} time=%{time_total}s\n" \
http://<公网IP>/
# 如果输出 000 + 超时 → 安全组未放行第二步:在云控制台放行端口
阿里云 ECS(控制台路径):
ECS 控制台 → 安全组 → 入方向规则 → 添加规则:协议 TCP,端口 80/80,来源 0.0.0.0/0
阿里云 CLI:
aliyun ecs AuthorizeSecurityGroup \
--SecurityGroupId sg-xxx \
--IpProtocol tcp \
--PortRange 80/80 \
--SourceCidrIp 0.0.0.0/0 \
--region cn-hangzhouAWS CLI:
aws ec2 authorize-security-group-ingress \
--group-id sg-xxx \
--protocol tcp --port 80 \
--cidr 0.0.0.0/0GCP:
gcloud compute firewall-rules create allow-http \
--allow tcp:80 --source-ranges 0.0.0.0/0第三步:验证
# 外网再打一次
curl -sS -o /dev/null -w "HTTP %{http_code}\n" http://<公网IP>/
# 期望:HTTP 200
# 日志中应出现真实客户端 IP
tail -1 /var/log/nginx/access.log安全组规则是实时生效的,不需要重启 nginx 或任何服务。放行后外网立刻可达,access.log 里也会立即出现客户端真实 IP。
如果以上都做了还不通,在 RHEL / AlmaLinux / CentOS 系机器上再查一层:
getenforce # 若为 Enforcing
ausearch -m avc -ts recent | grep nginxSELinux 可能阻止 nginx 绑定非标准端口或访问特定目录。
一句话外卖
云主机「本地通、外网不通」先查云平台安全组,access.log 没有外部 IP = 包没进 VM,nginx/iptables 都是无辜的。