KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
04 · 服务器与 nginx 骨架:先让 80 端口接住流量 — keel 龙骨
本章目标:用 IP 直连能拿到你的页面,且这个结果不依赖域名。 验收标准:curl -I http://203.0.113.10/ 返回 200(或 301),而不是超时。
本章目标:用 IP 直连能拿到你的页面,且这个结果不依赖域名。
验收标准:curl -I http://203.0.113.10/返回 200(或 301),而不是超时。
为什么单独设一章:HTTPS 失败的案例中,超过一半根本不是证书问题,而是"80 端口没通 / nginx 没匹配到 vhost / 服务没在跑"。先把 http 跑通,HTTPS 只是在它之上加一层。
一、三层放行,缺一不可
云服务器的端口可达性有三层开关,从外到内:
① 云平台安全组(控制台) → 不放行,TCP 根本到不了机器
② 系统防火墙(iptables/ufw/云镜自建链)→ 不放行,包到了也被丢
③ 进程监听(nginx listen) → 没监听,端口无响应
① 安全组
在云控制台的「安全组」里放行:
入站:TCP 22 来源:你的出口 IP(不要 0.0.0.0/0)
入站:TCP 80 来源:0.0.0.0/0
入站:TCP 443 来源:0.0.0.0/0
出站:全部放行(默认通常是)
22 端口只对你的 IP 开放,这是防止被暴力破解的第一道闸。家庭宽带的出口 IP 会变,变了之后需要更新——更稳的做法见本章末尾。
② 系统防火墙
# 查看当前规则(重点看是否有针对 80/443/22 的 DROP/REJECT)
sudo iptables -L -n --line-numbers
sudo iptables -L YJ-FIREWALL-INPUT -n --line-numbers 2>/dev/null # 云厂商自建链常见名
# 放行 80/443(iptables 直写)
sudo iptables -I INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -I INPUT -p tcp --dport 443 -j ACCEPT
# 用 ufw 的话
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status
如果发现类似 -s <你的IP> ... -j REJECT 的规则(云主机安全 agent 自动写入),删掉它并在链首插入 ACCEPT 短路:
sudo iptables -D YJ-FIREWALL-INPUT <规则行号>
sudo iptables -I YJ-FIREWALL-INPUT -s <你的IP> -j ACCEPT
③ nginx 监听
sudo nginx -t # 语法检查,永远先跑这个
sudo systemctl status nginx
sudo ss -lntp | grep -E ':80|:443'
ss 的输出里能看到 nginx 在监听,说明第三层没问题。
二、一个能用的站点骨架
Debian/Ubuntu 的约定:/etc/nginx/sites-available/<name> 写配置,/etc/nginx/sites-enabled/<name> 做软链启用。
# /etc/nginx/sites-available/learning
server {
listen 80;
server_name www.xxxxxx.cn xxxxxx.cn;
root /home/ubuntu/fqm/projects/learning-platform/public;
index index.html;
# 证书校验通道:必须保留在 http 这一侧,不能被 301 走
location /.well-known/acme-challenge/ {
root /var/www/html;
default_type "text/plain";
}
# 静态资源长缓存(文件名带 hash 才敢这么设)
location /assets/ {
expires 30d;
add_header Cache-Control "public, immutable";
}
location / {
try_files $uri $uri/ /index.html; # SPA 路由回退
}
access_log /var/log/nginx/learning.access.log;
error_log /var/log/nginx/learning.error.log;
}
启用:
sudo ln -s /etc/nginx/sites-available/learning /etc/nginx/sites-enabled/learning
sudo nginx -t && sudo systemctl reload nginx
nginx -t 是唯一的护栏:任何一次改动前先跑 -t,失败就不要 reload。配置写错 reload 会让 nginx 直接起不来——那个时刻你的站点是彻底不可用的。可靠做法是先备份再改:
sudo cp /etc/nginx/sites-enabled/learning /tmp/learning.bak
# 改完
sudo nginx -t || sudo cp /tmp/learning.bak /etc/nginx/sites-enabled/learning # 失败即回滚
三、三个高频坑
坑 1:root 与 alias 的尾部斜杠
location /learning/ { root /var/www; } 会把 URI 拼到 root 之后:/var/www/learning/index.html。
改成 alias /var/www/site/; 时,alias 是替换:/learning/index.html → /var/www/site/index.html。
判断方法很简单:看 404 的 error log,nginx 会把它实际去找的文件路径打出来。这条日志是排查静态资源 404 最快的入口:
sudo tail -20 /var/log/nginx/learning.error.log
# 会看到 open() "/xxx/xxx" failed (2: No such file or directory)
坑 2:SPA 路由刷新 404
前端用了前端路由(React Router 等)时,/learning/courses/tools/lessons/1/ 这个路径在磁盘上不存在,nginx 会返回 404。解法就是上面配置里的 try_files $uri $uri/ /index.html;——把所有未命中都回退给入口 HTML,由前端路由接管。
坑 3:部署后浏览器还是旧版本
静态资源带了 hash 一般没问题,但 index.html 常被 CDN 或浏览器缓存。上线后验证时加随机串:
curl -s "https://www.xxxxxx.cn/learning/?v=$(date +%s)" | head -20
更根本的做法是给 index.html 设 Cache-Control: no-cache(每次校验,但允许 304)。
四、SSH 通道的稳定性(顺手做,后面会救命)
后续的证书部署、内容发布都要走 SSH。用密码登录在公网上会被持续的爆破扫描命中,且云厂商的主机安全会把这类尝试判定为攻击并自动封禁你的 IP——结果是你在最需要上线的时候连不上服务器。
更稳的做法是密钥认证:
# 本地生成(Windows 用 Git Bash 或 paramiko,避免权限问题)
ssh-keygen -t rsa -b 2048 -f ~/.ssh/your_server_key
# 公钥内容追加到服务器 ~/.ssh/authorized_keys,权限是硬要求
chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys
# /etc/ssh/sshd_config 里关闭密码认证(确认密钥能登录之后再做)
PasswordAuthentication no
sudo systemctl reload sshd
⚠️ 关闭密码认证之前,务必另开一个终端窗口验证密钥能登录。把自己锁在门外是这一节唯一的致命风险。
五、本章验收
# 1. 配置语法
sudo nginx -t
# 2. 本地直连(绕过 DNS)
curl -I -H 'Host: www.xxxxxx.cn' http://127.0.0.1/
# 3. 外网直连 IP
curl -I --max-time 5 http://203.0.113.10/
# 4. 走域名
curl -I --max-time 5 http://www.xxxxxx.cn/
四条全部返回 200/301(而不是超时或 502),说明链路已通。
注意第 2 步与第 3 步的区别:本地通、外网不通 → 安全组/防火墙;本地不通 → nginx 或应用本身。
到这一步,你的站点已经能在 http 下访问了。下一章开始申请证书,把它升级成 https。