应避免直接使用 http.servefile,因其不支持缓存头与 range 请求;推荐用 http.fileserver 配合自定义中间件设置 cache-control、etag 和正确处理范围请求。

用 http.ServeFile 直接服务静态文件?别急,它不支持缓存头和范围请求
直接调用 http.ServeFile 看似省事,但 CDN 节点必须控制 Cache-Control、支持断点续传(Range 请求),而它默认不设缓存头,也不处理 If-None-Match 或 ETag。真实浏览器反复拉同一个 JS 文件时,会绕过本地缓存重发完整请求——这不是“静态服务”,是“静态裸奔”。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 改用
http.FileServer+ 自定义http.Handler中间件,手动写Cache-Control: public, max-age=31536000(对.js/.css/.woff2等资源) - 对图片等可能被频繁更新的资源,用
max-age=86400+ETag(基于文件内容哈希生成) - 务必检查
Content-Length和Content-Range是否在Range请求下正确返回——否则视频/大包下载会卡死
缓存策略:内存缓存 vs 本地文件缓存,选哪个?
CDN 节点不是纯代理,要主动缓存源站响应。Go 原生没有线程安全的 LRU,sync.Map 又不支持容量淘汰,硬上容易 OOM。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 小流量场景(QPS github.com/hashicorp/golang-lru/v2 的
lru.Cache,设置Size: 1000,键为request.URL.Path + request.Header.Get("Accept-Encoding") - 大流量或需持久化:跳过内存缓存,直接写入本地磁盘(如
/var/cache/cdn/<hash>.bin</hash>),用os.Stat检查ModTime判断是否过期,避免重复读文件 - 千万别把整个响应体(含 body)塞进 map——HTTP header 和 body 应分离缓存,body 用文件存,header 用内存存,否则 GC 压力陡增
net/http.Transport 配置不当,源站连接池会拖垮整个节点
CDN 节点要频繁回源,如果 http.Transport 没调优,会出现大量 connection refused 或 timeout,甚至把源站打挂。
修正说明:1,实现真正的软件开源。2,安装界面的美化3,真正实现栏目的递归无限极分类。4,后台添加幻灯片图片的管理,包括添加,修改,删除等。5,修正添加新闻的报错信息6,修正网站参数的logo上传问题7,修正产品图片的栏目无限极分类8,修正投票系统的只能单选问题9,添加生成静态页功能10,添加缓存功能特点和优势1. 基于B/S架构,通过本地电脑、局域网、互联网皆可使用,使得企业的管理与业务不受地域
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 必须设置
MaxIdleConns: 100、MaxIdleConnsPerHost: 100、IdleConnTimeout: 30 * time.Second - 禁用
ExpectContinueTimeout(设为 0),避免小请求卡在 100-continue 等待 - 加一层
context.WithTimeout到每个client.Do,超时设为3 * time.Second(源站响应慢就快速失败,别堵住连接池) - 错误日志里一定要打印出
req.URL.Host和err.Error(),否则你根本分不清是源站崩了还是 DNS 解析失败
路径安全:别让 ../../../etc/passwd 从 URL 里逃出来
用户请求 /static/..%2f..%2f..%2fetc%2fpasswd,若没做路径规范化,filepath.Join 可能拼出越权路径。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 收到请求路径后,先用
path.Clean归一化,再用strings.HasPrefix检查是否以允许前缀(如/static/)开头 - 禁止使用
filepath.Join拼接用户输入路径——改用filepath.FromSlash+filepath.Clean+ 白名单校验 - 静态资源目录必须用绝对路径初始化(如
/data/www),不要用./static,避免相对路径语义漂移 - Linux 下注意
os.OpenFile的0444权限位,别误开写权限,否则缓存文件可能被恶意覆盖
最麻烦的从来不是怎么缓存,而是缓存失效时要不要删本地文件、删的时候会不会正被另一个 goroutine 读着——这种竞态不会报错,只会返回空内容或 500。动手前先想清楚清理时机和锁粒度。









