Fiber中SPA正常工作的关键是手动兜底未命中静态资源的请求:必须在所有路由(Static、API)之后用app.Use("*", handler)捕获剩余请求,检查路径后缀和API前缀,再c.SendFile("./dist/index.html")返回,顺序错误或路径不安全将导致刷新白屏。

Go HTTP 服务器要让 SPA 正常工作,关键不是“返回 index.html”,而是**所有未命中静态资源的请求都必须落到前端路由入口**——Fiber 里这事儿得手动兜底,不能靠 app.Static() 自动处理。
为什么 app.Static("/","./dist") 不行
app.Static() 只服务真实存在的文件。当用户刷新 /dashboard 时,Fiber 会去找 ./dist/dashboard 这个目录或文件,找不到就 404。它不会退回到 index.html,因为这不是静态资源匹配逻辑的职责。
- 它不拦截 404,只负责“有就发,没有就跳过”
- 即使你把
index.html放在根路径,/api/users这类真实 API 请求也会被它错误捕获(如果./dist/api/users不存在,它仍可能 fallback 到 index.html) - 它无法区分“前端路由路径”和“后端 API 路径”,容易造成路由污染
正确做法:用 app.Use() 做兜底路由
必须在所有 app.Get()、app.Post() 和 app.Static() 之后,加一个通配中间件,专门捕获未被前面任何路由处理的请求,并返回 index.html。
- 顺序至关重要:静态资源 → API 路由 → 兜底中间件。放错位置会导致 API 被覆盖或静态资源被劫持
- 路径必须写成
app.Use("*", handler)或app.Use(func(c *fiber.Ctx) { ... }),不能用app.All(),否则会干扰 OPTIONS 预检等特殊请求 - handler 内部要用
c.SendFile("./dist/index.html"),不是c.File()或c.SendString();确保路径是相对于当前工作目录的绝对路径,或用filepath.Abs()校验
c.SendFile() 路径容易出错的三个点
Fiber 的 c.SendFile() 不做路径安全校验,传错路径就直接 500 或静默失败。
HISUI 前端框架基础开发指南。HISUI 是基于 easyui 二次开发的 jQuery UI 组件库,专为医疗信息系统设计。当涉及 HISUI 组件开发、jQuery easyui 表格/表单/布局/窗口、医疗系统前端页面编写时使用此 skill。 涵盖 40 个组件的官方文档参考,支持声明式与编程式两种初始化方式,提供 $cm/$m 数据访问方法。
立即学习“go语言免费学习笔记(深入)”;
- 别依赖
os.Getwd():运行时工作目录不确定,建议用filepath.Join(filepath.Dir(os.Args[0]), "dist", "index.html")或构建时 embed - 别漏掉
Content-Type:虽然c.SendFile()会自动推断,但若index.html被误判为text/plain,浏览器可能不执行 JS - 别在 handler 里用
http.ServeFile:那是标准库函数,和 Fiber 的*Ctx不兼容,会 panic
生产环境必须加路径白名单
兜底中间件不能无差别返回 index.html,否则 /favicon.ico、/robots.txt、API 错误路径都会被它吃掉,掩盖真实问题。
- 在兜底前先检查
c.Path()是否属于已知静态资源后缀:.js、.css、.png等,这些应直接 404 - 显式排除 API 前缀:
if strings.HasPrefix(c.Path(), "/api/") || strings.HasPrefix(c.Path(), "/health") { c.Next(); return } - 对根路径
/和带扩展名路径(如/manifest.json)做单独判断,避免误覆盖
最易被忽略的是:这个兜底逻辑必须放在所有其他路由注册完成之后,且不能被任何 next() 跳过——它不是中间件链的一环,而是最后的守门人。少一次 c.Next() 或多一次 return,SPA 就会在刷新时彻底失联。


















