从 Halo 到兰空图床,再到阿里云 ESA:配置重点与踩坑记录
前几天,我把 supoku 从一台干净的服务器一步步搭起来。Halo、兰空图床、阿里云 ESA,单看每一项都不复杂;真正把它们接在一起,问题往往出在两层之间。
这篇不是“复制粘贴即可用”的万能教程,更像是一份现场笔记:哪些配置值得固定下来,哪些坑我已经踩过,以及下次出问题应该先看哪里。
先保证能恢复,再考虑加速;先分清动态内容和静态资源,再决定缓存。
先把链路想明白
我现在采用的思路很简单:浏览器访问入口域名,边缘加速启用时先到 ESA,再由 Caddy 或 Nginx 回源。Halo 管文章和附件元数据,兰空图床管图片文件,上传插件负责在两者之间翻译 API。
浏览器 → ESA(启用时)→ Caddy / Nginx → Halo 或 Lsky Pro → PostgreSQL / 文件存储
这里有一点需要说清:目前 supoku.com 仍然直接解析到源站,没有把“计划接入 ESA”写成“已经上线”。ESA 这一部分主要记录图床接入和以后迁移时需要守住的边界。
Halo:能跑只是第一步
supoku 目前运行 Halo Pro 2.26.0、PostgreSQL 16 和 Caddy 2.11,由 Docker Compose 统一管理。公网只开放 80 和 443;Halo 的 8090、PostgreSQL 的 5432 都留在 Docker 私有网络里。
我最后保留下来的规则不多,但都很实用:
镜像固定到明确版本,不使用 latest。
数据库密码放在权限为 600 的 .env 中,不进 Git,也不出现在截图里。
升级前同时备份 PostgreSQL 和 Halo 文件目录,升级后再跑健康检查。
小内存机器保留 Swap,给 Halo 足够的启动时间。
监控使用 readiness 接口或真正的 GET 请求,不拿 HEAD / 的结果代替整站状态。
证书失败,问题却不在服务器
最费时间的一次故障是 DNSSEC。父区里残留了 DS 记录,但权威 DNS 没有对应的 DNSKEY。Let’s Encrypt 在真正连接服务器之前就拒绝了验证,所以我怎么改 Caddy 都没有用。
如果不准备启用 DNSSEC,就把失效的 DS 删除;如果要启用,就让 DS 和 DNSKEY 完整匹配。看到证书签发失败时,先查 DNSSEC,比盯着 80/443 端口有效得多。
2 GB 内存不能按大机器的习惯配置
这台机器可用内存不多。我没有再叠 Redis、Elasticsearch 或 APM,而是给系统配置了 2 GB Swap,并把 Halo 的启动等待时间放宽。Halo 启动需要几十秒并不异常,健康检查的 start_period 太短,反而会造成反复重启。
拉取镜像也遇到过国内网络问题。可以使用可信的国内镜像源做传输,但版本号和最终镜像仍要核对,不能因为“拉下来了”就默认它是正确版本。
Walker 的 layout.html 警告
Walker 1.19.0 在 Halo 2.26 后台提示缺少 templates/layout.html。原因是主题没有提供 Halo 页面布局契约所需的 html(head, content) 片段。补齐兼容文件后,后台已经显示“页面布局已支持”。
这个修改要同时保留备份和变更记录。主题属于应用市场专业版,修兼容问题不等于绕过授权;升级主题前也要确认自定义文件会不会被覆盖。
CSS 改了,页面为什么没变
答案最后很朴素:缓存。品牌 CSS 独立放在主题之外,能避免主题升级覆盖,但 Caddy 给静态资源设置了 7 天缓存。文件内容已经更新,页面仍引用 supoku.css?v=1,浏览器当然继续使用旧对象。
这次把地址改成 supoku.css?v=2 后样式立即生效。以后每次改静态资源,我都会同步修改版本参数,而不是只清浏览器缓存。
兰空图床:改域名不能只改 DNS
Lsky Pro 的 APP_URL 会参与生成公开链接;反向代理传入的 Host、Scheme 和 X-Forwarded-Proto 又决定应用是否认为当前请求是 HTTPS。两边只要有一个不一致,就可能出现“上传成功,但返回的是 http 地址”或者后台仍跳回旧域名。
我会逐项核对下面这些配置:
APP_URL 使用最终的 HTTPS 域名,例如 https://img.example.com,不附加 API 路径。
站点根目录指向项目 public 目录,绝不直接暴露项目根目录和 .env。
代理正确传递 Host、X-Real-IP、X-Forwarded-For、X-Forwarded-Proto;HTTPS 回源时同时检查 SNI。
修改 .env 后重启 Web 容器和队列进程,避免后台任务继续使用旧配置。
迁移旧域名前先统计历史绝对 URL,替换后抽查旧文章,不能只验证新上传图片。
上传大小也不是改一个参数就结束。ESA、反向代理、PHP 和 Lsky Pro 都可能拒绝请求。出现 413、423,或者源站应用日志里完全没有请求时,我会先向上排查代理和边缘层。
还有一个容易误判的现象:图片能在浏览器里打开,不代表前端脚本一定能复制它。HTTPS、CORS、Canvas 安全限制和响应头都可能影响剪贴板操作。
Halo 兰空上传插件:为什么用了 Fork
我参考的是 LQBBY 的 halo-lsky-pro Fork。上游插件主要面向开源版 Lsky Pro 的 v1 API;商业版使用 /api/v2/,接口路径、上传参数和返回结构都不一样。只替换 URL 不够,上传可能成功,Halo 却解析不了结果。
这个 Fork 增加了 apiVersion 选择,并处理了几处关键差异:
v1 上传使用 strategy_id,v2 使用 storage_id。
v2 的公开地址来自 public_url,状态可能是字符串 success,而不是布尔值 true。
图床地址只填写站点根地址;插件会自己拼接 /api/v1 或 /api/v2,避免出现 /api/v2/api/v2。
商业版 Token 在 Lsky Pro 后台创建;只给必要权限,并固定插件版本、安装包来源和提交版本。
它也不是没有限制。Lsky Pro 只适合作为图片附件后端,不能满足 Halo 的缩略图要求;商业版 v2 没有插件可用的删除端点,所以在 Halo 删除附件,不会同步删除图床文件;v2 响应没有 size,Halo 中的附件大小可能显示为 0。开启图片压缩或格式转换后,显示大小也可能与原图不一致。
我的验收方式不是只点一次“验证图床设置”。还要从 Halo 附件页上传一张测试图,刷新后台,打开文章,再确认返回地址是 HTTPS。删除测试附件时,也要记得检查图床端是否仍保留原文件。
阿里云 ESA:最容易混淆的是 Host 和 SNI
ESA 配置里,“源站地址”“回源 Host”“回源 SNI”是三件事:源站地址决定连哪台服务器,Host 决定命中哪个虚拟主机,SNI 决定 TLS 握手使用哪张证书。
一台 IP 承载多个 HTTPS 站点时,Host 和 SNI 都应该明确指向源站证书覆盖的域名。这里配错,常见表现不是一个统一错误,而是 404、证书不匹配,或者 525/526。
我给 ESA 留下的几条边界是:
不要把已接入 ESA 的加速域名再设为它自己的源站,避免回源环路;使用源站 IP 或独立的 origin 域名。
图片、CSS、JS 可以长缓存;Halo 后台、登录、API、上传、预览,以及带 Authorization 或 Cookie 的请求默认绕过缓存。
HTML 返回 no-cache、no-store 或 private 时,不用自定义规则强行覆盖。
发布新样式后,刷新或预热准确的版本化 URL;supoku.css?v=1 和 ?v=2 是两个缓存对象。
上传接口要放行实际使用的 HTTP 方法,同时检查 WAF、Bot 规则、请求体大小和超时。
上线后我会直接看 X-Site-Cache-Status,而不是凭体感判断“加速是否生效”。如果图片上传失败、源站又没有任何日志,大概率是请求在 ESA 或反向代理这一层就被拦下了。
我现在的排障顺序
把顺序固定下来之后,排查会快很多:
先看 DNS:A/CNAME、TTL、DNSSEC 是否符合预期。
绕过 ESA 直连源站,验证 GET、TLS 和证书。
检查反向代理的 Host、SNI、X-Forwarded-Proto、上传大小和超时。
检查 Halo external URL、Lsky APP_URL、插件 API 版本、Token 与 storage_id。
最后再查 ESA 的回源、缓存、WAF 和 X-Site-Cache-Status。
日志也按链路从外向里看:边缘日志 → 代理日志 → Halo/Lsky 日志 → 数据库和存储。哪一层完全没有记录,问题通常就在它的上游。
写在最后
这次搭建之后,我更愿意把博客看成一套需要长期维护的小系统,而不是装完主题就结束的网站。版本、备份、域名、证书、缓存和附件存储互相牵连,真正省时间的是留下可复现的记录。
以后每次升级或迁移,我都会补上三件事:为什么改、怎么验证、出问题怎么退回去。等这套流程稳定下来,博客本身才算真正属于自己。
参考资料
Halo 官方 Docker Compose 文档:https://docs.halo.run/getting-started/install/docker-compose
Lsky Pro 官方安装文档:https://docs.lsky.pro/guide/install
Lsky Pro 常见问题:https://docs.lsky.pro/guide/faq
LQBBY:Halo 兰空图床商业版支持 Fork:https://www.lqbby.com/archives/halo-lsky-pro-lskypro-v2-api
阿里云 ESA 回源 Host:https://help.aliyun.com/zh/edge-security-acceleration/esa/user-guide/origin-fetch-host
阿里云 ESA 默认缓存规则:https://help.aliyun.com/zh/edge-security-acceleration/esa/user-guide/default-cache-rule

