static中间件需显式配置index为index.html才能支持spa刷新,挂载路径应限定前缀(如/assets),browse仅对目录末尾斜杠生效,compress必须置于static之后。

static 中间件必须显式配置 Index 才能支持 SPA 刷新
SPA(如 Vue、SvelteKit、React Router)在浏览器中刷新时,会向服务端请求真实路径(如 /dashboard/settings),但该路径对应的是前端路由,后端并不存在这个文件。若不处理,static 中间件直接返回 404。它默认只服务物理存在的文件,不会自动 fallback 到 index.html。
解决方法是启用 Index 配置项,并确保它指向你的入口 HTML:
-
static.New("./public", static.Config{Index: "index.html"})—— 这会让所有未命中文件但匹配目录的请求,返回./public/index.html - 路径必须存在且可读,否则仍 404;
index.html文件名大小写敏感 - 不要把
static挂在根路径(如app.Use(static.New("./public"))),否则每个请求(包括/api/login)都会先被 static 尝试匹配,拖慢路由分发
Use() 的前缀决定静态资源生效范围,不是“过滤器”
app.Use() 第一个参数是挂载点,不是通配规则。它决定了哪些请求会进入该中间件,且不自动向下传递到其他路由。
-
app.Use("/assets", static.New("./public"))→ 只响应/assets/logo.png、/assets/css/app.css,而/favicon.ico或/health完全不经过它 -
app.Use(static.New("./public"))(无前缀)→ 所有请求都先进 static,找不到才进路由;这会干扰 API 路由,也容易暴露/etc/passwd类路径(若用户能控制路径) - 生产环境建议严格限定前缀,例如
/static或/assets,并与前端构建输出路径对齐
Browse: true 不等于“提供文件列表 API”
设 Browse: true 后,访问 /assets/(末尾带斜杠)且该路径是真实目录、但无对应 index.html 时,会返回 HTML 格式的目录索引页。这不是 JSON 接口,也不可编程控制。
- 访问
/assets(无斜杠)或/assets/logo.png(文件存在)→ 不触发 Browse,按常规文件逻辑处理 - 返回的是纯 HTML,无 CORS 头、无缓存控制、不可嵌入 iframe(受 X-Frame-Options 限制)
- 真要实现
GET /api/files?path=assets这类结构化目录接口,得自己写 handler,用os.ReadDir("./assets")+ 路径白名单校验(如strings.HasPrefix(reqPath, "./assets"))
compress 中间件不能放在 static 前面
compress.New() 是基于响应体内容做 gzip/brotli 压缩的,但它在 static 之后执行才能拿到实际文件内容。如果顺序反了,static 返回的文件响应会被跳过压缩,或者因 Content-Length 已定导致压缩失败。
- 正确顺序:
app.Use(compress.New())放在app.Use("/assets", static.New(...))之后 - 若用
app.Use("/assets", compress.New(), static.New(...)),则 compress 只对 /assets 下的响应生效,更精准 - 注意:compress 对已压缩的二进制文件(如 .jpg、.png)无效,但对 .js/.css/.html 效果明显
static 中间件看似简单,但 Index 回退、挂载路径、Browse 行为和压缩顺序这四点一旦配错,就会出现刷新 404、API 被拦截、目录意外暴露或资源未压缩等问题——它们都不报错,只是静默失效。











