跳到正文
一个页面请求怎样到达我的 Astro 站点
一个页面请求怎样到达我的 Astro 站点

一个页面请求怎样到达我的 Astro 站点

按当前部署还原 s1oopX 站点的真实链路:Cloudflare 边缘、Tunnel、回环 Nginx、静态发布目录,以及 OAuth 和部署 webhook 的动态分支。

速读#

我以前把一次请求画成 DNS、Cloudflare、Tunnel、Nginx、后端、数据库再返回的固定七站,既容易让人误以为是 OSI 七层,也不符合当前站点。迁移到 Astro 后,普通文章是构建期生成的静态 HTML,正常请求不会经过业务后端和数据库;如果 Cloudflare 已经缓存了资源,甚至不会到达源站。只有 OAuth、部署 webhook 等少数路径会由 Nginx 转给本机 Node 服务。

把真实链路画清楚后,排障不再是把所有技术都检查一遍,而是先确认这次请求走了哪个分支:边缘缓存、静态源站、OAuth,还是部署接口。请求链解决“用户怎样读到内容”,发布链解决“新内容怎样进入当前目录”,两条链有关联,却不能混成一条。

当前公开页面的真实路径#

用户访问 https://s1oopx.bond/posts/... 时,正常路径可以写成:

浏览器
-> DNS 返回 Cloudflare 边缘地址
-> Cloudflare 边缘终止 HTTPS,并执行已配置的缓存/WAF 规则
-> 命中缓存:直接返回,不访问源站
-> 未命中或不可缓存:沿 Tunnel 转发
-> cloudflared 的出站连接
-> 127.0.0.1:80 上的 Nginx
-> /var/www/astro-narrow/current 中的静态 HTML、CSS、JS 或图片
-> 响应沿各段连接返回浏览器

这里没有一个贯穿浏览器到 Nginx 的单一 TCP 连接。浏览器连接 Cloudflare,Cloudflare 再通过 Tunnel 把请求送到源站,各段分别建立连接;“沿原路返回”只是逻辑方向相反,不代表端到端连接没有被终止。TLS 对访客在 Cloudflare 边缘终止,Tunnel 自身再保护边缘到 cloudflared 的传输,Nginx 因此可以只监听本机回环 HTTP,而不公开 Web 入站端口。

各组件的职责也应准确分开:

位置负责什么不负责什么
DNS把主机名解析到 Cloudflare 边缘不证明源站 IP 永远无法被其他记录或历史发现
Cloudflare 边缘访客 TLS、缓存、WAF 与边缘路由不保证源站应用和静态文件一定正常
Tunnel让源站主动建立出站通道不替代访问授权、进程守护和应用健康检查
Nginx按主机名与路径提供文件或转发到本机服务不生成 Astro 页面,也不保存文章数据
发布目录保存某个已构建版本的静态产物不负责自动构建、切换和回滚

当前 Nginx 的 root 指向 /var/www/astro-narrow/current,这个路径是发布脚本切换的当前版本。/_astro/ 下带内容哈希的资源返回长期不可变缓存头,普通页面则按实际响应策略由 Cloudflare 判断。因为站点是静态生成,文章内容来自构建时的 Markdown,而不是请求时查询 MySQL 或 SQLite。

同一个域名下还有几条动态分支#

“静态站”不等于域名下没有服务端代码。管理后台页面本身是静态文件,但登录入口 /oauth 会由 Nginx 转发到 127.0.0.1:4180 的 OAuth 服务;它生成 GitHub 授权请求、校验一次性 state、换取用户信息并限制允许账号。/deploy/github 则转发到 127.0.0.1:4181 的 webhook 服务,由它对 GitHub 原始请求体验签、检查事件和目标提交,再触发受控部署。

因此不同路径的依赖不同:

请求源站分支额外依赖
/posts/.../_astro/...Nginx 读取静态文件当前发布目录和磁盘
/admin/index.htmlNginx 读取静态 CMS 页面Cloudflare Access 策略;登录后还依赖 GitHub
/oauth/oauth/callbackNginx → OAuth Node 服务GitHub OAuth、环境变量和允许名单
/deploy/githubNginx → webhook Node 服务GitHub 签名、部署脚本、Git 与构建环境
/healthz/health.jsonNginx 读取最小健康文件当前发布目录;health.json 还记录构建 revision

看到主页正常,不能推出 OAuth 服务正常;反过来,OAuth 挂掉也不应该影响读文章。路径拆分的价值就在这里:一个故障只影响它实际依赖的分支,排查也应从具体 URL 开始。

缓存命中会让源站完全看不到请求#

排障时最容易漏掉的分支是边缘缓存。如果某个静态资源返回 CF-Cache-Status: HIT,Cloudflare 可以直接回答,Nginx 访问日志和源站进程都不会出现这次请求。反过来,MISSDYNAMIC 或绕过缓存时,请求才可能继续走 Tunnel;具体值要结合 Cache Rules、响应头和资源类型解释,不能把所有非 HIT 都当成故障。

从外部先检查响应链:

Terminal window
curl -I https://s1oopx.bond/
curl -I https://s1oopx.bond/_astro/<实际资源文件>

重点看状态码、重定向、Cache-ControlAgeCF-Cache-Status 和 Cloudflare Ray ID。若怀疑缓存掩盖源站问题,可以请求一个确定不缓存的健康地址,或按受控方式清理单个 URL;不应在没有证据时全站 purge,因为那会让所有资源同时回源,反而制造新的负载。

我现在怎样按层收缩故障#

“网站打不开”先从外部和源站两端取证,而不是按固定顺序重启所有服务:

检查目标命令或动作能说明什么
DNSResolve-DnsName s1oopx.bonddig s1oopx.bond域名是否返回预期边缘地址
公网 HTTPcurl -I https://s1oopx.bond/边缘是否能返回状态码、是否发生重定向或缓存
Tunnelsystemctl status cloudflared、查看对应 journal源站到 Cloudflare 的出站通道是否在线
Nginx 与静态文件curl -I -H 'Host: s1oopx.bond' http://127.0.0.1/绕过边缘后,本机入口与当前发布目录是否正常
当前版本readlink -f /var/www/astro-narrow/currentNginx 实际服务的是哪个 release
OAuthcurl -fsS http://127.0.0.1:4180/healthzOAuth 进程是否在本机响应
Webhookcurl -fsS http://127.0.0.1:4181/healthz部署 webhook 进程是否在本机响应

如果公网失败而本机 Nginx 正常,范围在 DNS、Cloudflare 规则、Tunnel 或主机名路由;如果本机 Nginx 也失败,再看 Nginx 配置、current 链接、文件权限和发布目录。某个动态路径失败时,只检查对应 Node 服务和外部依赖,不因为 OAuth 失败就怀疑静态文章数据库——当前架构根本没有这一步。

仓库还保留了一条公开侧烟雾检查,可以在部署完成后从外部验证主页、后台边界、OAuth、无签名 webhook 和健康接口:

Terminal window
SMOKE_BASE_URL=https://s1oopx.bond pnpm production:smoke

自动检查不能替代日志,但能证明几个关键分支至少表现出预期状态,比只在服务器本机执行 curl localhost 更接近用户视角。

发布链与请求链不要混在一起#

用户请求静态页面时不会运行 Astro 构建。新提交先经过 GitHub 工作流验证,符合条件的 workflow_run webhook 到达部署服务,部署脚本拉取指定提交、安装依赖、构建到新的 release 目录、执行检查,再原子切换 current 链接;失败时保持旧版本或执行回滚。Nginx 只是继续读取 current 指向的文件,不知道背后发生过多少构建步骤。

Git push
-> GitHub Actions 验证
-> 已签名 webhook
-> 本机部署服务
-> 新 release 构建与检查
-> current 链接切换
-> 后续请求读到新文件

把两条链分开后,问题也更容易命名。页面返回旧内容,可能是边缘缓存或 current 没切换;构建成功但公网 404,可能是产物路径、Nginx try_files 或 base 配置;部署 webhook 不工作,不代表当前线上静态文件会立即消失。每个症状都对应有限的观察点。

这张图还暴露了什么缺口#

当前站点仍是单源站、单 VPS。Cloudflare 边缘有分布式能力,不能等同于源站高可用;主机损坏后仍需要靠代码仓库、环境配置、备份和部署文档重建。这个规模下我不急着增加多区域源站和数据库集群,更优先验证发布回滚、服务代码复制、环境文件权限、异地备份和恢复步骤。

系统理解也因此从“我学过 DNS、Nginx、Docker 和数据库”变成了“我知道当前一个具体请求实际经过哪些组件,以及哪些组件根本不会参与”。链路图的价值不是层数多,而是每条箭头都能用一次请求、一个日志或一条命令验证;架构变化后,图也必须跟着改,不能让旧经验继续冒充现状。

版权许可

CC BY-NC-SA 4.0 本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

相关文章

s1oopX

登录 s1oopX