这个站点没有 CMS,也没有数据库驱动的前台渲染:前台是构建期生成的纯静态 HTML,后台是一个 Go 写的单二进制接口服务。这篇把它讲清楚,方便以后自己回看,也方便想搭个人站的人抄作业。
一、整体结构
浏览器 ──HTTPS──> Nginx ──┬── 静态文件(个人站 HTML/CSS/JS,构建期生成)
├── /api/ ──> tools.linux(Go 单二进制,127.0.0.1:8080)
├── /f/ ──> filesrv(文件服务,图片直链)
└── platform.ruilin.xin ──> platform-core 平台
- 前台:Next.js 静态导出(
output: "export"),所有页面在构建期渲染成 HTML,Nginx 直接吐文件; - 后台:
/admin是同一份静态产物里的页面,登录后调 API,文章、站点配置都存在 MySQL 里; - 图片:走独立的文件服务,前台只拿到一个直链 URL,不占应用服务器的磁盘与内存。
二、为什么前台要用静态导出
个人站最大的流量来源是搜索引擎,而爬虫不想执行 JavaScript。静态导出的结果是:每一篇文章、每一个工具页,HTML 里就有完整正文,百度直接抓走即可。
代价是「内容变更 → 需要重新构建」。我的处理方式:
- 首页、列表页这类会变的区块,构建期写一份快照进 HTML,客户端挂载后再按接口刷新一次——读者看到的是最新的,爬虫拿到的是快照;
- 真正频繁变化的东西(博客正文)在构建时逐篇抓取,正文直接进 HTML;
- 构建末尾有一道产物自检:只要发现占位页、空区块,构建直接失败、不许发布(吃过亏,见下面)。
三、后端为什么坚持单二进制
一个静态编译的 Go 程序 + 一个 TOML 配置,由 supervisor 托管,重启就是几秒的事:
supervisorctl stop tools
install -m 0755 tools.linux /root/.../backend/tools.linux
supervisorctl start tools
它负责:接口(内容、搜索、留言、统计)、限流与防爆破、后台鉴权、以及“构建期的数据源”。 没有容器、没有编排,2 核 2G 的机器跑一个站绰绰有余。
四、上线流程
- 后端交叉编译
GOOS=linux GOARCH=amd64,scp 上传后替换二进制并重启; - 前端
node scripts/build.mjs prod(构建前删掉.next/cache,否则旧数据会被缓存进产物); - 构建产物打 tar 上传,服务器上整目录替换前端目录;
nginx -t && nginx -s reload;- 线上自检:首页 200、文章页有正文、sitemap 条数、接口正常。
五、踩过的坑
- 构建期被限流打成空壳:接口按 IP 限流(10 req/s),而构建要并发抓 1600+ 个接口。结果
next build显示成功,但 1500 多篇文章页里一多半是没有正文的占位壳。现在构建期改用本机后端连生产库,并且构建末尾强制自检,不合格直接失败。 - 混合内容:切 HTTPS 后,数据库里还存着 http 的图片地址,浏览器直接拦掉不显示。所有落库的 URL 都要跟着域名一起改。
- 验证文件放错目录:搜索引擎的验证文件以前放在前端目录里,而前端每次部署整目录替换——验证就失效了。现在统一放
/var/www/verify/,用 nginx 正则 location 直出。
六、后面想做的
- 文章内的图片懒加载与图集排版;
- 站内搜索覆盖博客(现在只搜知识库);
- 部署脚本化:一条命令完成构建、自检、上传、替换、回滚。
站点是自己给自己写的,慢慢来。