为什么要做静态资源缓存和防盗链
打开一个普通的企业站或者个人博客,页面上体积最大的往往不是 HTML 本身,而是图片、CSS、JavaScript 这些静态资源。一张未压缩的图片动辄几百 KB,如果每次访问都要重新下载,页面加载速度会非常难看,服务器带宽和流量也会被白白消耗。Nginx 作为高性能的 Web 服务器,处理静态文件的能力远强于 PHP 这类动态程序,只要配置得当,大部分静态请求根本不需要落到后端。本文分享个人站长最实用的两组 Nginx 配置:静态资源的长效缓存和防盗链,配合验证方法,照着做就能明显感觉到网站变快、流量变省。
第一步:给静态资源设置长效缓存
浏览器缓存是性能优化里性价比最高的一环。图片、CSS、JS 这类资源的特点是几乎不变,完全可以告诉浏览器放心存上一两个月。Nginx 里用 expires 指令就能输出 Cache-Control 和 Expires 响应头,配置写在 server 块里。需要注意 location 的匹配优先级:用 ~* 开头的正则匹配会覆盖前缀匹配,所以像下面这样把静态文件的后缀列出来,精确命中要缓存的对象,不会误伤动态页面。
location ~* \.(css|js|png|jpg|jpeg|gif|webp|svg|ico|woff2?)$ {
expires 30d;
# 若想标记 immutable,改用下面这行(与 expires 二选一,避免重复头):
# add_header Cache-Control "public, max-age=2592000, immutable";
access_log off;
log_not_found off;
}这段配置的含义是:匹配到的静态文件缓存三十天,并告诉浏览器在有效期内可以直接使用本地副本。access_log off 关掉静态文件的访问日志,因为图片请求量大,写日志既占磁盘又费 CPU,出了问题时这些日志参考价值也不大。这里有一个重要的经验:CSS 和 JS 文件更新频率比图片高,如果缓存时间太长,发布新版本后用户浏览器里还是旧文件,导致页面样式错乱。解决方法是给文件名加上版本号或者内容哈希,比如 app.20260901.js,文件名变了浏览器自然会重新下载,缓存期就能放心设长。
关于 expires 指令有两个细节值得了解。第一,expires 会同时输出 Expires 和 Cache-Control 两个响应头,现代浏览器以 Cache-Control 的 max-age 为准,Expires 是给 HTTP/1.0 老客户端兜底用的。第二,如果你还想输出 immutable 之类的额外标记,注意 Nginx 的 add_header 指令有一个继承规则:当前层级一旦出现 add_header,外层的同名指令就不会再输出;而且 expires 与手动添加的 Cache-Control 并存会产生两个同名响应头,属于不规范的做法。所以要么只用 expires 30d,要么只用 add_header 写完整的 Cache-Control 头,二选一即可,别两个都写。另外要特别强调,强缓存只应该加在带版本号或内容基本不变的静态资源上,HTML 页面本身不要开强缓存,否则网站更新后用户看到的永远是旧页面,HTML 用 no-cache 走协商缓存才是正确的做法。
第二步:开启 gzip 压缩传输体积
缓存解决的是重复访问的问题,第一次访问的流量还得靠压缩来省。gzip 是 Nginx 内置的压缩模块,对文本类资源效果显著,CSS、JS、HTML 的压缩率通常能达到百分之七十以上。配置放在 http 块里,主要参数有 gzip on 开启功能,gzip_types 指定要压缩的 MIME 类型,gzip_min_length 设置小于多少字节不压缩,因为太小的文件压缩后反而更大,gzip_comp_level 是压缩级别,个人服务器建议设 5,级别再高 CPU 开销增大收益却很小。
gzip on;
gzip_vary on;
gzip_min_length 1k;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript
application/xml image/svg+xml font/woff2;两个容易踩的坑提醒一下:第一,图片格式里只有 SVG 适合 gzip,jpg、png、webp 本身已经是压缩格式,再压一遍只会浪费 CPU,所以 gzip_types 里不要写 image/jpeg 和 image/png。第二,开启 gzip 后要确认响应头里真的带上了 Content-Encoding: gzip,有些网站程序自己先压缩过一遍,Nginx 再压就会出现双重压缩甚至乱码,如果发现异常,检查后端是否已经开启了压缩,两者留一个即可。新版的 Nginx 还支持 brotli 压缩算法,压缩率比 gzip 更高,但需要额外编译模块,追求极致优化再考虑。
第三步:open_file_cache 减少磁盘重复打开
高并发场景下,Nginx 每处理一个静态文件请求就要打开一次文件、读取一次元数据,磁盘 IO 压力不小。open_file_cache 指令可以把文件描述符、文件大小和修改时间缓存起来,命中缓存的请求直接跳过 open 系统调用。配置同样放在 http 块,max 指定最多缓存的条目数,inactive 指定条目多久没被访问就淘汰,配合 open_file_cache_valid 设置检查间隔。
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;个人网站流量不大时这段配置的效果不明显,但流量起来之后收益很实在,属于一次配置长期受益的选项。open_file_cache_min_uses 2 表示文件被访问两次以上才进入缓存,避免把只访问一次的文件也塞进缓存占用内存。配置完记得用 nginx -t 检查语法,然后 nginx -s reload 平滑重载,不会中断现有连接。
第四步:防盗链配置,防止图片被白嫖
网站流量上去之后,另一个让人头疼的问题是图片被别的网站直接引用。别人把图片地址贴到自己的页面里,加载时消耗的是你的带宽和流量,这就是盗链。Nginx 防盗链的基本原理是检查请求头里的 Referer 字段,用 valid_referers 指令声明合法的来源,不在名单里的来源一律拒绝。
location ~* \.(png|jpg|jpeg|gif|webp|ico)$ {
valid_referers none blocked server_names
*.example.com example.com;
if ($invalid_referer) {
return 403;
}
}valid_referers 后面的参数含义要理解清楚:none 表示允许没有 Referer 的请求,也就是用户直接在浏览器地址栏输入图片地址或者从收藏夹打开的情况;blocked 表示允许 Referer 被防火墙或隐私插件去掉的请求;server_names 表示允许本站域名;后面再列出你自己的域名和子域名。$invalid_referer 变量由 Nginx 根据规则自动计算,值为 1 就说明来源不合法。有人喜欢把盗链图片替换成一张警告图,用 rewrite 指向警告图片地址即可,但要注意别做成循环跳转,简单起见直接返回 403 更干净。
第五步:认清 Referer 防盗链的局限
必须坦诚地讲,基于 Referer 的防盗链只能防君子不防小人。Referer 是浏览器主动发送的,攻击者完全可以伪造,用 curl 加一个 -e 参数就能带上任意来源。爬虫、下载工具和恶意用户根本不在乎你的防盗链规则。所以如果图片涉及商业利益,比如付费素材或者会员图库,建议在 URL 里加入带时效的签名参数,服务器校验签名和时间戳,这才是真正可靠的方式,代价是实现复杂一点。对绝大多数个人站长来说,图片被零星盗链造成的流量损失其实很有限,用 Referer 规则挡住搜索引擎图片收录带来的盗链大头就够用了,不必过度设计。
验证配置是否生效
配置完成后,用 curl 加 -I 参数请求一个静态文件,查看返回的响应头,这是最直接的验证方式。Cache-Control 或 Expires 存在且数值正确说明缓存生效,Content-Encoding: gzip 说明压缩生效。防盗链的验证方法是用两个请求对比:一个带本站 Referer,一个带外站 Referer,后者应该返回 403。
curl -I https://你的域名/wp-content/uploads/2026/09/demo.png
# 期望看到 cache-control: max-age=2592000 或 expires 头
curl -I -H "Referer: https://evil-site.com" https://你的域名/wp-content/uploads/2026/09/demo.png
# 期望看到 HTTP/1.1 403验证时注意浏览器可能已经缓存了旧响应,用 curl 不带缓存地请求能拿到最新结果。检查 gzip 是否生效还有一个小坑:curl 默认不发送 Accept-Encoding 头,服务器自然不会返回压缩内容,需要加上 --compressed 参数让 curl 声明支持压缩,或者手动加 -H "Accept-Encoding: gzip" 再查看响应头里的 Content-Encoding。另外,如果网站套了 CDN,CDN 节点会优先响应请求,你看到的可能是节点上的缓存策略而不是源站的真实响应头,源站的缓存和防盗链配置需要同步到 CDN 的回源策略里,否则用户在节点上命中缓存,源站的防盗链规则根本不会执行。
小结
静态资源优化是个人网站性能调优里投入产出比最高的部分:expires 设置长效缓存减少重复下载,gzip 压缩减少传输体积,open_file_cache 降低磁盘开销,valid_referers 挡住盗链流量。四组配置加起来不到三十行,不需要改任何业务代码,却能实实在在地提升加载速度、节省带宽流量。配置的先后顺序建议是:先开 gzip,再设缓存,最后加防盗链,每加一步都用 curl 验证一次,确保每一步都生效后再进行下一步,这样出了问题也容易定位。