http.fileserver默认返回404或存在目录遍历风险,根本原因是未用http.stripprefix剥离url前缀且http.dir路径未设为绝对路径;正确做法是先stripprefix(如"/static/")再传入os.dirfs("./public")构建的http.fs,确保路径安全映射。

直接用 http.FileServer 很容易出 404 或目录遍历漏洞,关键不在“怎么起服务”,而在“路径怎么处理”和“根目录怎么设”。
为什么 http.FileServer 默认不工作?
常见现象是访问 /static/style.css 返回 404,哪怕文件真实存在。根本原因是:http.FileServer 接收的是请求路径的**完整 URL 路径**,但它的底层 http.Dir 只负责按字面拼接,不做任何路径裁剪。
比如你注册了:
http.Handle("/static/", http.FileServer(http.Dir("./assets")))
当请求 /static/js/main.js 时,http.FileServer 会尝试打开 ./assets/static/js/main.js(多了一层 static),而不是你期望的 ./assets/js/main.js。
- 必须用
http.StripPrefix先去掉注册前缀,再交给http.FileServer -
http.Dir的路径必须是静态文件所在的真实根目录,不能带前缀 - 路径末尾斜杠必须一致:注册路径带
/,否则子路径会 404
如何安全地提供当前目录下的 public 文件夹?
这是最常见需求,但也是最容易被绕过的场景——不加限制的 http.Dir 允许 ../ 回溯,可能泄露源码或配置文件。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
正确做法是用 http.FS + os.DirFS(Go 1.16+)替代裸 http.Dir,它默认拒绝路径遍历:
fs := http.FS(os.DirFS("./public"))
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(fs)))
-
os.DirFS构建的fs.FS天然禁止..跳转,比手动清理路径更可靠 -
http.StripPrefix必须写在http.FileServer外层,顺序不能反 - 如果用旧版 Go(http.Dir + 自定义
http.FileSystem做白名单校验,但没必要硬扛
为什么访问 / 会看到目录列表?
这是 http.FileServer 的默认行为:当请求路径对应一个目录且无 index.html 时,返回可点击的文件列表。生产环境几乎从不想要这个。
- 加个空
index.html到根目录,能覆盖掉列表页,但只是掩耳盗铃 - 真正解决方式是用自定义
http.Handler拦截目录请求,统一返回 404 或重定向到/index.html(SPA 场景) - 更轻量的做法:用
http.FileServer配合http.NotFoundHandler做 fallback,但注意它不拦截目录访问,只管文件不存在
例如 SPA 前端常用模式:
fs := http.FS(os.DirFS("./public"))
fileServer := http.FileServer(fs)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, err := fs.Open(r.URL.Path)
if os.IsNotExist(err) && !strings.HasSuffix(r.URL.Path, "/") {
// 尝试找同名 .html
if _, err2 := fs.Open(r.URL.Path + ".html"); err2 == nil {
http.ServeFile(w, r, "./public"+r.URL.Path+".html")
return
}
// 否则一律 fallback 到 index.html
r.URL.Path = "/index.html"
}
fileServer.ServeHTTP(w, r)
})
性能与生产部署要注意什么?
http.FileServer 是标准库实现,没有缓存、压缩、ETag 等功能,全靠操作系统 page cache 和 Go runtime 的基础优化。
- 小文件(io.Copy +
http.ServeContent控制分块和 range 请求 - 不要在
http.FileServer前加反向代理(如 Nginx)还开启 gzip——重复压缩浪费 CPU,应由前端服务器统一处理 - 开发时用
http.ListenAndServe没问题;生产务必加超时控制,或改用http.Server显式设置ReadTimeout/WriteTimeout
路径处理逻辑一旦写错,要么 404,要么越权读取。宁可多写两行 StripPrefix 和 os.DirFS,别图省事用裸 http.Dir。










