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。

进入 keel 阅读