记一次让人崩溃的部署踩坑:Nginx 缓存引发的血案
一个小小的缓存问题,我从下午折腾到晚上。真的是气死我了,搞得我还试了各种各样的方法。说实话我还是第一次碰到这样奇奇怪怪的问题的,算是长记性了。
起因
博客写完了,准备部署到服务器上。前端是 Next.js 16,后端是 FastAPI,服务器用的阿里云宝塔面板。
本来以为是个简单活——打包、上传、配置 Nginx 反向代理,完事。结果这个"简单活"让我从下午一直折腾到晚上,差点没把我气死。
第一个坑:API 地址到底怎么配
Next.js 有个机制:以 NEXT_PUBLIC_ 开头的环境变量会被打包到前端代码里。我的想法很简单——开发环境用 http://localhost:8000,生产环境留空走 Nginx 反代。
于是我写了:
apiBaseUrl: process.env.NEXT_PUBLIC_API_URL || "",
.env.development 里写 http://localhost:8000,.env.production 里留空。
打包,上传,刷新——请求还是发到了 http://boke.hiromu.top:8000。
我???
反复改了七八种方案:?? 换 ||、空字符串、硬编码、运行时检测……没一个管用的。后来才想明白,Next.js 的环境变量在 SSR 和客户端的行为不一致,空字符串可能被当成 undefined,然后 fallback 到默认值。
最后的解决方案:直接用 Next.js 的 rewrites 配置代理,完全不用环境变量。
// next.config.ts
async rewrites() {
return [
{ source: "/api/:path*", destination: "http://127.0.0.1:8000/api/:path*" },
];
}
siteConfig.ts 里直接写死 apiBaseUrl: ""。开发环境 Next.js 通过 rewrites 代理到后端,生产环境 Nginx 直接转发,请求根本到不了 Next.js。
就这么简单、干净、没有歧义。早该这么干!
第二个恶心人的地方:编译产物没问题,但浏览器就是不对
解决了代码问题,打包上传,重启——还是不行。
我在本地 grep 编译产物,确认 apiBaseUrl:"" 没问题。但浏览器 Network 面板里清清楚楚显示请求发到了 http://boke.hiromu.top:8000。
我都开始怀疑人生了,后来在服务器上跑 grep,发现编译产物确实是空字符串。那浏览器发的 8000 是哪来的?后面我又试了些方法还是不行,突然我就发现!我一进入网站是在首页,然后不管我点击哪个导航页面的请求都发不出去,都是奇奇怪怪的http://boke.hiromu.top:8000开头,但是但是!!只要是直接在其他页面直接刷新就可以有数据,然后切换到主页有数据显示,但是刷新又没数据又发不了请求了,奇怪的要死。我做了很多测试在浏览器上,fetch('/api/posts?status=published&page=1&size=4').then(r=>r.json()).then(d=>console.log('数据:', d.length)).catch(e=>console.log('错误:', e)),像是这样看看有没有数据,结果就是在主页也可以发请求拿到数据。就离谱!直接刷新就报错
关键现象
- 编译产物里完全没有
http://boke.hiromu.top:8000或localhost:8000 curl https://boke.hiromu.top/api/health返回{"status":"ok"},Nginx 反代正常- 浏览器控制台
fetch('/api/albums')能成功返回数据 - 清除浏览器缓存、无痕模式、换浏览器都不行
- 只有首页出问题,其他页面刷新后正常
答案是:Nginx 缓存了旧的 HTML 页面。
0.yx~-n01gczc.js 这个文件名是旧的编译产物。每次 next build,Next.js 会根据代码内容生成带 hash 的 chunk 文件名。旧 HTML 里引用的是旧文件名,新 .next 里是新文件名。Nginx 把旧 HTML 返回给浏览器,浏览器去找旧 chunk——文件不存在,直接报错。
而且这个缓存不是浏览器缓存,是 Nginx 服务端缓存。清浏览器缓存、无痕模式、换浏览器——全都没用,甚至我把项目文件夹内容全删一遍,重新打依赖,重新打包,依旧有缓存,我也不知道为什么这个缓存能这么厉害,反正 Nginx 就是不给你新页面。
解决方案
在 Nginx 配置的 location / 里加几行:
proxy_no_cache 1;
proxy_cache_bypass 1;
proxy_cache off;
add_header Cache-Control "no-cache, no-store, must-revalidate";
重载 Nginx,刷新——世界清净了。
血的教训
- 部署问题先查缓存。 如果 curl 正常但浏览器不对,八成是缓存问题。不要反复改代码。不要不要!
- Next.js 的环境变量坑很深。 前后端分离项目用 rewrites 代理 API,不要用环境变量拼接 URL。
- 宝塔面板的 Nginx 可能有隐式缓存。 你没配缓存不代表它没缓存,
proxy_cache_path可能在别的配置文件里。 - 编译产物和运行时是两回事。 代码里没有
8000不代表浏览器不会发8000的请求——可能是旧代码在作祟。
最后的最后
从下午两点半搞到晚上八点多,一个 Nginx 缓存问题。
有时候 debug 最难的不是找到 bug,而是接受"问题不在你以为的地方"。我一直盯着代码改,反复确认编译产物,但问题根本不在代码里——它在代码和浏览器之间的某个缓存层里。
下次再遇到类似问题,第一件事:curl 对比,查缓存。



