fiber 静态文件 404 是因路径挂载错误或配置失当:app.static() 严格按前缀字符串替换,static.new() 需显式挂载且不剥离前缀;防盗链、路径校验等中间件必须前置,目录存在性与缓存头需手动保障。

静态文件 404 不是 Fiber 没能力,而是没挂对路径、没配对模式——app.Static() 和 static.New() 行为完全不同,混用或错序直接导致资源不可达。
app.Static() 路径匹配必须严格对应目录结构
它不是“把整个目录映射过去”,而是按前缀做字符串替换:浏览器请求 /static/css/main.css,Fiber 就去查 ./public/css/main.css(假设你写的是 app.Static("/static", "./public"))。
- 如果文件实际在
./assets/css/main.css,就必须写app.Static("/static", "./assets"),否则/static/css/main.css一定 404 -
./public末尾不能带斜杠(./public/),某些 Go 版本会 panic;Windows 下用/或\都行,但路径字符串本身要一致 - 工作目录决定相对路径起点:用
go run main.go启动时,./public是终端当前路径下的子目录,不是main.go所在目录 - 想避免歧义?改用绝对路径:
filepath.Join(os.Getenv("PWD"), "public")或filepath.Dir(os.Args[0])+"../public"
static.New() 必须显式挂载且顺序不能错
static.New() 是终结型 handler,一旦命中就返回文件、不走后续中间件。它也不支持前缀路径自动剥离,app.Use(static.New("./public")) 只能响应 GET /favicon.ico 这种根路径请求,而 GET /static/logo.png 完全不匹配。
- 要支持
/assets/*→./public/assets/*,必须写app.Use("/assets", static.New("./public/assets")),前缀/assets和挂载点必须完全一致 - 防盗链、日志、路径遍历校验等中间件,必须放在
static.New()之前注册,否则不会执行 - 目录不存在时它不报错,只默默返回 404;上线前务必加
os.Stat()检查 - 默认不防
../../../etc/passwd—— 要启用安全模式,得手动设.EnableDirListing(false).Browse(false),或在前置中间件里拦截含".."的c.Path()
/favicon.ico 必须单独处理
浏览器发的是根路径请求 GET /favicon.ico,app.Use(static.New("./public")) 因无前缀无法命中;app.Static("/static", "./public") 也管不到它。
- 最简方案:
app.Static("/favicon.ico", "./public/favicon.ico"),Fiber 内部会把它转成单文件服务 - 更可控的写法:
app.Get("/favicon.ico", func(c *fiber.Ctx) error { data, _ := os.ReadFile("./public/favicon.ico"); return c.Type("image/x-icon").Send(data) }),可加缓存头、fallback 逻辑 - 别忘了
/apple-touch-icon.png和/apple-touch-icon-precomposed.png,iOS 设备会额外请求这两个路径 - CDN 或 Nginx 反向代理时,确认它们没截断或覆盖这些请求
缓存头、MIME、路径遍历这些细节容易漏掉
app.Static() 默认加了 Cache-Control,但 static.New() 完全不设;两者都不校验目录是否存在、都不过滤非法路径、都不验证 Referer —— 这些都不是“可选优化”,而是生产环境的硬性要求。
真正生效的关键不在“有没有调用”,而在“路径是否精确定位、中间件是否前置、目录是否存在、缓存是否合理”。少一个环节,就可能被扫出敏感文件,或让前端反复拉取未缓存的 CSS。











