三个月前,我看了眼支付宝账单,光服务器续费就划走了 998 元。那台 4C8G 的云主机,为了扛住一天不到 200 的 UV,已经陪我烧了两年钱。

说实话,这账怎么算都不划算。

Day 1 我做了个决定:要么彻底关站,要么把成本打下来。最后我选了后者,用 Nuxt 3 重写前端,Cloudflare Pages 托管,加上 R2 存图片,成本从每月 998 元干到 5 元。

月 5 元,不是笔误,是 Cloudflare 的 R2 存储费——10GB 以下免出口流量,我超了一点点,按 0.15 美元/GB 月计费,折合人民币约 5 元。


为什么是 Nuxt 3,不是 Next.js 也不是纯静态

我上一版站是 Vue 2 + 自建 API,前后端分离,但 SEO 一直半死不活。Google 收录 37 页,百度更惨,9 页。原因我清楚:客户端渲染,蜘蛛进来全是空壳。

换 Next.js 也想过,我团队里有人用 React 写了八年,但个人站要重学 App Router,还要处理 React 生态的版本地狱。我们评估了一下,迁移成本至少 14 天全职工时。

Nuxt 3 不一样,它用 Nitro 引擎,天生支持混合渲染。你可以在同一个页面里,对未登录用户输出静态 HTML,对登录用户走 SSR。

数据对比:我用相同内容分别渲染 100 次,Nuxt 3 的 TTFB 平均 212ms,Next.js 14 是 245ms。差距不大,但 Nuxt 3 的配置简单 40%,不需要额外配 rewrites。

回头看,选 Nuxt 3 最大理由是它和 Cloudflare Pages 的兼容性。Nitro 官方支持 preset: 'cloudflare_pages',构建后直接生成 _worker.js,不需要额外的适配层。Next.js 也有适配器,但坑多,后面会讲。


架构拆解:静态 + SSR + 边缘函数

我不需要 100% 动态。我的站有 4 种内容类型:

  • 博客文章:每周 2-3 篇,基本不变
  • 订阅管理:用户登录后可见,动态
  • 评论系统:实时写入,动态
  • 简历页:半年不变

按照 Cloudflare 的免费层,Pages Functions 每天 10 万次请求,静态资源不计数。我的全站月 PV 约 8 万,静态页面占 92%,动态请求集中在评论和订阅,每天约 1500 次。

Day 7 我确定了最终架构:

  1. 博客文章和简历页走静态生成(SSG),构建时拉取内容
  2. 订阅和评论走 SSR,用 useAsyncData 请求 API
  3. 登录态用 Cloudflare KV 存 session,过期时间 7 天

这个架构的妙处在于,Pages 会把静态文件部署到全球 300 个边缘节点,而动态请求只在最近节点执行函数。


踩坑记录:三个让我熬夜到凌晨 3 点的坑

坑一:Prisma 在边缘环境跑不起来

Day 3 我尝试用 Prisma 连接 Postgres 存评论数据,本地开发一切正常。部署到 Cloudflare Pages 后,Functions 运行时直接报错。

困惑:本地好好的,生产环境就是 Error: PrismaClient is unable to be run in edge runtimes。

搜了一天,找到答案:Pages Functions 默认跑在 V8 isolates 里,不支持 Prisma 的二进制引擎。解决办法有两个,换掉 ORM,或改用 D1(Cloudflare 的 SQLite)。

我选了 D1,因为免费额度 5GB 存储,且和 KV 一样免维护。

D1 的坑不比 Prisma 少。SELECT * FROM comments WHERE post_id = ? 这种最简单的查询,第一次执行竟然要 400ms。后来发现是冷启动,加了 preparedStatement 缓存后稳定在 12ms。

坑二:图片优化导致页面权重暴跌

上个月我把图片全部迁移到 Cloudflare Images,原因是为了省 R2 流量费。结果一周后,Google Search Console 提示 Core Web Vitals 的 LCP 从 1.8s 涨到 4.2s。

现象是页面代码里 <img> 标签的 src 变成了 CDN 地址,但每次请求都要先走 Worker 做图片变换。我用了 on-the-fly 的参数 ?width=800&format=webp,结果每个图片首次访问要等 Worker 处理完才返回。

解法:放弃 on-the-fly,改为构建时压缩。在 Nuxt 3 的 nitro 配置里加一个 hook,本地生成三种尺寸的 WebP 文件,直接放 R2。图片加载时间从 4.2s 降到 0.8s。

坑三:Pages Functions 的并发限制

第 2 周,我的站被某个技术社区转载了,当天 UV 冲到 8500。然后,订阅接口开始 500 错误。

看日志,Function invocation limit exceeded。Cloudflare 免费层的限制是每秒 1000 次函数调用。我的评论系统在高峰期每秒触发 2400 次写操作,直接打爆。

解法不是升级付费套餐,而是加缓存。我把热门文章的评论在构建时静态化,写入 JSON 文件,动态接口只处理 POST /api/comments 的实时提交。读取评论全部走静态文件,24 小时重建一次。


数字对比:三个月的实测数据

指标 旧架构 (云主机 + 自建 API) 新架构 (Nuxt 3 + Pages) 变化
月成本 (元) 998 5.2 -99.5%
TTFB 中位数 (ms) 386 212 -45%
LCP (ms) 2100 760 -63%
构建部署时间 (min) 手动 rsync,平均 15 CI 自动,平均 3 -80%
月故障时间 (小时) 2.4 (包含挖矿入侵) 0 -100%
服务器 CPU 使用率 平均 7%,峰值 62% 无服务器 不再关心

说实话,看到这组数据的时候我愣了几秒。不是得意,是怀疑哪里算错了。但连续跑了 30 天,成本端确实只有 R2 的超额费用 5 元,其他全为 0。


成本到底怎么算到 5 元的

Cloudflare 免费层包含:无限静态请求、每天 10 万次函数调用、KV 每天 10 万次读、D1 每天 5 万次行读取。这些都够我用。

唯一的超额项是 R2。免费层给 10GB 存储 + 每月 100 万次读操作。我的图片总量 13.7GB,超了 3.7GB,存储费 0.15 美元/GB/月,所以 0.56 美元,约合 4 块钱。再加一点读操作超额,总共 5.2 元。

如果我把图片压到 10GB 以内,成本就是 0。

不过呢,我没压,因为质量还是第一位的。而且按我现在 3 万张图片的增长速度,至少 8 个月后才会再超额度 1GB。


Nuxt 3 配置细节:三个我试过值得分享的参数

  1. routeRules:把博客详情页设为 swr: 3600,表示 1 小时重新验证一次。我试过 ssr: false,那等于回到纯客户端渲染;也试过 static: true,但文章修改后要重新构建。SWR 是折中最优解。

  2. nitro.prerender.crawlLinks:打开后会自动抓取站内所有链接并静态化,省掉手动维护路由列表。我跑了一次,自动预渲染了 487 个页面。

  3. build.transpile:如果你们用了某些老库(比如我用的一个 markdown 解析器),不加这个配置,构建产物会报错的。这个我花了 3 天踩坑。


但我也要承认 Nuxt 3 + Cloudflare 不如 传统主机 的地方

第一,调试体验差。传统主机上你 ssh 进去,tail -f 日志,什么都在眼前。Cloudflare Functions 的日志要到仪表盘看,每条调用延迟 30 秒。而且本地调试和线上行为有差异,尤其是 KV 的一致性。我遇到过线上读到旧值,本地永远复现不了,最后只能加 await env.KV.get(key, {cacheTtl: 0}) 强制穿透。这点比不上直接在服务器上 redis-cli 看一眼。

第二,构建时间有变长的趋势。Day 1 构建 2 分钟,现在 200 篇文章后需要 5 分钟。如果内容量再翻 10 倍,构建可能要 20 分钟。传统主机不需要构建,直接跑代码就行。

第三,长期成本的不确定性。Cloudflare 免费层已经持续了很多年,但没人保证永远免费。万一哪天调整策略,把 Functions 改成按秒计费,我的成本会瞬间涨到 80 元/月。而自建服务器,成本是确定的。

第四,锁仓风险。我现在的内容数据库在 D1(Cloudflare 的 SQLite 变体),照片在 R2,代码是 Nuxt 3。要是想迁回自建,需要重写数据库层,还要导出 13GB 的 R2 文件,加上换用别的渲染方式。整体迁移成本,我估算了一下,大约是 5 个工作日。自建主机没这问题,搬走就是 scp -r。


我们团队最后做了什么

第 6 周,我们把同样的架构复制到了另外两个项目上。一个内部工具站,一个客户门户。成本合计 9 元/月,之前这两个项目光容器服务就要 420 元。

我并不是说所有站都适合这种方案。如果你的接口有强一致性要求,或者需要处理 WebSocket,Cloudflare 的免费层就捉襟见肘。我的站没有这些需求,所以才能把成本压到极致。

回头看,那个 998 元/月的时期,服务器实际处理的有效请求不到十分之一。现在用 5 块钱买到了更好的性能,这不是技术胜利,是认知问题:你总以为复杂系统需要昂贵的载体,直到你被迫用 5 块钱重新设计一次。

如果你也在跑个人站,看看账单里那 998 元,你确定每一分都花得有价值吗?