网站在自己的电脑上能够打开,是一个令人愉快的时刻。页面、字体、图片和交互终于组合到了一起,看起来已经“做好了”。
但本地能打开,与互联网上任何人都能稳定访问,是两件不同的事。
真正的部署要回答更多问题:文件放在哪里?域名怎样找到它?证书由谁签发?更新失败时如何回退?浏览器为什么仍然显示旧页面?后台保存的内容又存在哪里?
Mullwise 的上线过程让我意识到,部署不是最后一次上传文件,而是建立一条可以重复、检查和撤销的发布路径。
一、先分清网站由哪些部分组成
最简单的网站只需要 HTML、CSS、JavaScript 和图片。这些文件可以直接放到静态托管平台,不需要一台长期运行的传统服务器。
但 Mullwise 后来增加了文章管理、留言、实时信号、邮件和图片上传。此时网站已经不只是一组静态文件,而是由几层能力组成:
text
浏览器页面
↓
静态资源:HTML / CSS / JavaScript / 字体 / 图标
↓
边缘逻辑:文章接口、登录、留言、邮件与抓取
↓
数据与文件:D1 数据库 / R2 图片存储
把这些部分分开很重要。静态资源适合被全球缓存,文章和留言需要数据库,上传图片需要对象存储,而管理员身份验证需要在服务端完成。不是所有功能都应该塞进同一种工具里。
二、第一版网站不一定需要传统服务器
许多人一想到部署,就会先购买 VPS,然后安装 Linux、Nginx、数据库和运行环境。这样当然可以,但它同时带来系统更新、端口、防火墙、进程守护、备份和故障恢复等维护工作。
如果第一版主要是品牌页面、文章和少量动态功能,静态托管加边缘函数通常更轻。
Mullwise 使用 Cloudflare Pages 承载页面,并用 Worker 处理动态请求。它带来的直接好处是:
- 静态文件不需要自己维护服务器;
- HTTPS 证书可以自动管理;
- 网站会分发到靠近访问者的节点;
- 每次部署都有独立版本,出问题时可以回退;
- 后续可以继续接入 D1、R2 和其他服务。
这类架构并不适合所有项目,但它很适合一个正在成长的个人网站:先把维护成本压低,把时间留给内容和功能。
三、源代码和部署产物不是一回事
一个项目通常同时包含源代码、配置、开发工具和最终要上传的文件。
真正部署到网站的,应该是构建完成后的产物。例如 Mullwise 的公开目录中包含:
text
index.html
admin/
css/
js/
assets/
favicon.svg
_worker.js
这里有一个非常具体、也很容易忽略的细节:上传压缩包时,网站文件应当位于压缩包根目录。
错误的结构是:
text
dist/
index.html
css/
assets/
正确的结构是:
text
index.html
css/
assets/
如果多包了一层目录,平台可能仍然提示“部署成功”,但访问根地址时找不到 index.html,CSS、图标和文章图片也会全部落到错误路径。部署系统只知道文件上传完成,并不知道你的目录结构是否符合网站预期。
所以,每次发布前都应该先打开压缩包,看一眼第一层目录。这十秒钟的检查,往往比部署后的排查更有价值。
四、Pages 与 Worker 各自负责什么
Pages 最擅长直接提供静态文件。Worker 则可以在请求到达时执行逻辑,例如:
- 从数据库读取已发布文章;
- 判断管理员是否已经登录;
- 保存用户留言;
- 调用邮件服务;
- 定时抓取外部信息源;
- 为文章生成动态页面。
浏览器访问首页时,HTML、CSS、字体和图标应尽量直接作为静态资源返回;访问 /api/articles 或某篇动态文章时,再由 Worker 处理。
这条边界能让网站保持简单。如果所有请求都进入复杂逻辑,静态页面也会变得难以缓存和排查;如果所有内容都做成静态文件,后台管理和实时数据又会很笨重。
部署设计不是选择一个万能工具,而是让每一层只承担自己擅长的事情。
五、自定义域名要在托管平台和 DNS 两边同时成立
网站通过 pages.dev 地址能够访问,并不代表自定义域名已经完成。
要让 mullwise.com 正常工作,至少要同时满足:
1. Pages 项目已经绑定 mullwise.com;
2. DNS 中存在正确记录,并指向对应项目;
3. Cloudflare 已经确认域名所有权;
4. HTTPS 证书已经签发;
5. Worker 和静态资源能够正确识别自定义域名请求。
如果 Pages 地址正常,而自定义域名的 CSS 或图片返回 404,问题通常不在页面设计,而在资源路径、路由或静态资源绑定。
Mullwise 曾遇到过一种典型情况:动态页面能够返回,但静态资源请求使用了错误的主机或目录,于是页面只剩下没有样式的文字,图标也无法显示。最终修复的关键不是重新设计页面,而是让自定义域名下的静态资源请求统一回到正确的 Pages 资源地址。
这类问题说明:“首页能打开”并不是完整的验收标准。
六、一次部署至少要检查五种页面
每次发布以后,我会按固定顺序检查:
1. 首页:导航、字体、图标和主要板块是否正常;
2. 文章列表:公开文章是否齐全,草稿是否隐藏;
3. 文章详情:页首、正文、图片和页尾是否与首页一致;
4. 管理后台:能否登录、编辑、保存并重新读取;
5. 静态资源:CSS、JavaScript、图标和文章图片能否直接访问。
还应该分别测试根域名、www 和平台提供的默认域名,并至少用手机或另一台电脑访问一次。
不同设备的测试不是形式主义。它能暴露本机缓存、登录状态、系统字体和网络 DNS 缓存造成的差异。开发电脑上“一切正常”,有时只是因为它保存了最多的旧资源。
七、缓存既能加速,也会制造错觉
静态网站通常会为 CSS、JavaScript、字体和图片设置较长缓存。这能显著提高访问速度,但更新资源后,浏览器可能继续使用旧版本。
常见的解决方法,是给资源地址增加版本标记:
html
<link rel="stylesheet" href="/css/style.css?v=20261006-1">
<script src="/js/main.js?v=20261006-1"></script>
文件内容发生变化时,版本参数也随之变化,浏览器就会把它视为一个新资源。
但不能把所有问题都归咎于缓存。如果直接访问 CSS 地址得到的是 404,强制刷新不会创造一个不存在的文件;如果压缩包目录错误,清空缓存也不会修复路径。正确顺序是先确认服务器返回了什么,再决定是否处理缓存。
八、后台数据不能跟着页面文件一起覆盖
文章、草稿、留言和网站设置属于内容数据,不应该因为重新上传静态页面而消失。
Mullwise 把这些内容保存在 D1 数据库中,把图片保存在 R2。网站代码更新时,只替换页面与程序;数据库中的文章和留言继续保留。
这里需要特别避免一种设计:每次启动 Worker 都用内置文章覆盖数据库。更安全的初始化方式是“只在记录不存在时补齐”,之后以后台保存的数据为准。
同样,首页只展示有限数量的文章,也不等于旧文章被删除。首页、文章列表和数据库是三个不同层次:
- 首页决定推荐哪些内容;
- 文章列表决定公开展示哪些内容;
- 数据库保存全部文章,包括草稿。
把这三者混在一起,就很容易出现“新增文章覆盖旧文章”或“改成草稿后首页仍然存在”的问题。
九、发布以前必须准备回退方案
一次可靠发布不要求永远不出错,但要求出错以后能迅速恢复。
最低限度的回退能力包括:
- 代码保存在 Git 仓库,每次修改有清楚的提交记录;
- Cloudflare 中保留上一版成功部署;
- 数据库定期导出备份;
- DNS 修改前记录原值;
- 发布包能够重新生成,而不是只存在某个人的下载目录里。
页面样式出错时,可以回退到上一版部署;数据修改出错时,可以依据备份恢复;域名配置出错时,可以对照记录撤销。不同类型的问题需要不同的退路,不能只依赖一个“撤销”按钮。
十、我现在使用的发布清单
每次发布前:
- 确认只修改了计划中的文件;
- 检查 JavaScript 与 Worker 语法;
- 查看压缩包根目录;
- 为更新过的 CSS 和 JavaScript调整版本号;
- 确认没有把密码、令牌和私人信息打包进去;
- 保存当前可用版本与数据备份。
每次发布后:
- 检查部署状态是否成功;
- 分别打开首页、文章页、后台和静态资源;
- 验证自定义域名与 HTTPS;
- 用无痕窗口或另一台设备复查;
- 确认文章、草稿和留言数据仍然存在。
这个清单没有高级技巧,但它把“凭感觉发布”变成了可重复的过程。
十一、上线不是终点,而是建立节奏
一个网站真正可用,不是因为它曾经成功部署过一次,而是因为以后仍然可以安全地更新。
页面设计会继续变化,后台功能会继续增加,数据源也可能调整。只要发布流程保持清楚:代码在哪里、数据在哪里、域名指向哪里、出错怎样回退,网站就不会因为一次小修改变得不可维护。
对个人网站来说,最合适的技术不一定是能力最多的,而是几个月以后仍然愿意打开、理解并继续修改的那一种。
部署的最终目的,也不是把文件搬到云端。它是在本地创作和公开访问之间,建立一条可靠的路。