将静态资源压缩与边缘缓存按“先压缩后缓存、压缩级别取中间值、缓存键区分编码”三条原则协同配置,可以在动静分离架构下让站点加载速度提升一个量级。这套组合拳不是简单的“开个Gzip再挂个CDN”,关键在于静态资源从源站到边缘节点的全链路处理逻辑,下面直接拆解具体做法。
动静分离后静态资源压缩怎么做
动静分离架构里,静态资源通常交给Nginx或OSS处理,动态请求回源到应用服务器,静态资源压缩的本质是在带宽和CPU之间做交换,而边缘缓存的本质是让用户的请求在离他最近的节点直接拿到结果,两者叠加时,容易踩的坑是:边缘节点缓存了未压缩的版本,或者缓存了压缩版本但没告诉浏览器正确的编码格式。
选择压缩级别需要看资源类型
静态资源主要分两类:文本类(HTML、CSS、JS、SVG、JSON)和二进制类(图片、字体、视频),文本类适合Gzip或Brotli压缩,二进制类本身已经是压缩格式,再压一遍只会浪费时间。
业内共识认为,Brotli比Gzip的压缩率高出约15%到20%,但压缩速度更慢,实际配置时,建议按资源类型分开处理:
- HTML文档:用Brotli级别4到5,压缩率高且首字节延迟可控
- JS和CSS文件:用Brotli级别5,配合长期缓存策略
- 图片和视频:不启用动态压缩,只在源站做好体积优化
- API返回的JSON数据:在应用层启用Gzip级别6,避免边缘缓存参与动态内容
Nginx动态压缩与预压缩的取舍
源站Nginx有两种压缩方式:动态压缩(每次请求实时压缩)和预压缩(构建时生成.gz或.br文件),预压缩更适合配合边缘缓存,因为预压缩文件可以直接存储到缓存节点,边缘节点不需要重新压缩。
具体配置路径:
gzip on; gzip_types text/plain text/css application/json application/javascript; gzip_min_length 1024; gzip_comp_level 6;
如果使用Brotli,需要在Nginx中加载ngx_brotli模块:
brotli on; brotli_comp_level 5; brotli_types text/plain text/css application/json application/javascript;
预压缩模式更简单:构建工具生成.gz和.br文件后,Nginx配置gzip_static on和brotli_static on,源站直接返回预压缩文件,边缘节点回源时拿到的就是压缩过的内容,缓存下来直接对外分发。
边缘缓存与压缩配置协同的核心环节
边缘节点缓存静态资源时,缓存键(Cache Key)必须包含Content-Encoding信息,否则会出现一种情况:第一个用户用支持Brotli的浏览器请求,边缘节点缓存了.br版本,第二个用户用老版本浏览器请求,边缘节点直接返回了.br文件,浏览器无法解码导致白屏。
缓存键设计直接决定命中率
行业里常用的做法是在缓存键中加入Accept-Encoding头作为维度,以简米云CDN为例,配置路径是:CDN控制台 → 缓存配置 → 自定义缓存键 → 勾选“包含请求头Accept-Encoding”。
这样设置后:
- 支持Brotli的请求命中
.br缓存 - 只支持Gzip的请求命中
.gz缓存 - 两者互不干扰
多数情况下,开启这个功能后边缘节点缓存命中率能维持在90%以上,源站的压缩请求量会大幅下降。
Cache-Control头要同时考虑浏览器和边缘节点
浏览器缓存和边缘缓存是两个独立层,Cache-Control头的配置需要同时照顾到两者的需求,这里推荐一个常见组合:
| 资源类型 | 浏览器缓存 | 边缘节点缓存 | 说明 |
|---|---|---|---|
| HTML文件 | no-cache | no-cache | 需要每次回源校验 |
| JS/CSS | 1年 | 1年 | 文件名带哈希值 |
| 图片/字体 | 30天 | 30天 | 体积大,回源成本高 |
| JSON数据 | 1分钟 | 不缓存 |
具体响应头示例:
Cache-Control: public, max-age=31536000, immutable Content-Encoding: br Vary: Accept-Encoding
Vary头的作用是告诉边缘节点和浏览器,这个URL的内容会因请求头不同而不同,如果你的源站是Nginx,默认配置可能没带Vary头,需要在location块中手动添加。
缓存刷新策略要区分动态和静态
动静分离架构下,缓存刷新经常被搞成一刀切,静态资源更新依赖文件名哈希,哈希变了就是全新URL,不需要刷新;HTML文件用no-cache策略,每次回源校验后自然是最新的。
实际运维时注意两点:
- 发布新版本时,只刷新HTML入口文件的缓存,静态资源不要动
- 误操作刷新了整个目录时,边缘节点会同时回源大量请求,可能打爆源站,建议刷新后开启源站限流
边缘节点回源压缩率的动态调节机制
这一步是很多团队忽略的细节,边缘节点回源时,如果源站响应了压缩内容,边缘节点直接缓存该内容;如果源站响应了未压缩内容,边缘节点是否压缩后再缓存,取决于服务商的默认策略。
压缩率与首字节延迟的平衡
边缘节点在回源时对内容进行压缩会额外消耗时间,这个时间会叠加在首字节延迟(TTFB)上,虽然用户感觉不明显,但源站CPU资源有限,高峰期可能成为瓶颈。
一个比较务实的策略是:源站只做Gzip压缩,边缘节点开启Brotli压缩,原因是等价的CPU开销在不同位置产生的影响不同,源站CPU紧张会影响所有请求,而边缘节点通常有独立的压缩资源池,酷番云 EdgeOne 和 Cloudflare 的默认策略大致如此,据行业通用做法,大多数边缘云服务商会优先使用边缘节点的压缩能力。
图片类资源的边缘处理方案
图片压缩不适合用Gzip/Brotli处理,应该用专门的图像格式转换,边缘节点可以配置图片瘦身、WebP转换等能力,这里要提一个常见疑问:静态资源压缩率与图片格式转换的区别是什么?前者是无损压缩文本类资源,后者是有损压缩图片,两者针对的资源类型完全不同。
实际操作路径:
- 对于用户上传的图片,在源站转换为WebP和AVIF格式,结合URL参数回退到原图
- 边缘节点开启图片自适应格式,依据请求头如
Accept: image/avif动态返回最优格式 - 设置图片质量参数后保存为新的缓存键,使不同质量需求可以命中不同缓存
这种做法在电商网站和内容社区中应用较广,因为图片请求通常占整体静态资源的较大比例。
动静分离静态资源加速优化方法中容易忽略的细节
把压缩和边缘缓存协同好以后,还有一些细节在流量高峰期才暴露问题。
HTTP/2与边缘缓存的连接复用
边缘节点对同一客户端的多个请求可以复用一条TCP连接,但压缩后的响应体大小不同,造成TLS握手和连接复用的效率差异不太明显,真正的影响在多语言环境下Vary头的处理,如果源站根据Accept-Language返回不同内容,而边缘节点没有把该头纳入缓存键,同样会导致内容错乱。
的缓存策略
现在很多站点将主资源放在自家服务器,第三方字体和库走公共CDN或NPM镜像,这类方式对加载速度的提升有限,因为第三方域名涉及的DNS解析和TLS握手还是需要时间,在多域名HTTP/2加速方案中,Self-host是一件值得做的事情。
具体做法是:
- 将常用的前端库下载到自己的OSS上
- 使用Webpack的externals配置指向自建静态资源路径
- 在HTML入口文件的
<link rel="preconnect">中提前建立连接
使用这种方法,配合前文提到的预压缩和边缘缓存,可以最大化地减少外部请求链路。
访问日志中的状态码与命中率判断
验证配置的效果,不一定要看复杂的监控面板,直接查看边缘节点的访问日志,几个关键指标即可判断:
- X-Cache状态:HIT代表节点命中,MISS代表未命中回源
- 状态码304:浏览器已缓存,回源校验后确认使用缓存
- 响应体大小:如果所有JS都返回200且体积较大,说明压缩配置失效
查看日志的具体操作路径以简米云CDN为例:CDN控制台 → 日志管理 → 离线日志 → 下载后搜索所需字段,观察日志时先看MISS的比例,再检查回源请求的响应头里是否有Content-Encoding,如果没有,说明源站压缩没生效。
动静分离与边缘缓存协同常见问题
Q:源站开启了Gzip,边缘节点还需要配置压缩吗?
不需要,有一个原则需要理解:压缩只做一次,做在离源站最近的位置,源站已压缩,边缘节点直接透传即可,如果边缘节点再压一次会造成CPU浪费且可能改变响应头导致缓存键失效,反之,如果源站没压缩,边缘节点开启压缩功能,同时返回Content-Encoding: br或gzip,浏览器可以正常解压,两种情况,边缘节点都会按最终的编码结果作为缓存键的一部分。
Q:静态资源压缩率设置多少比较合适?性能和体积怎么权衡?
对于文本类资源,Gzip级别6是多数云厂商的默认值,Brotli级别5表现均衡,再高的压缩级别收益很小,且会显著增加CPU消耗,比如Brotli级别11的压缩率只比级别5高不到3%,但压缩时间多了数倍,日常配置时一个量化参考:压缩后的JS文件体积在响应头Content-Length中可以看到,如果文件缩小至原体积的三分之一左右,说明压缩已经足够;如果还在二分之一以上,需要检查是否有多语言或内联代码干扰了压缩,低级别配置适合源站负载较高的场景,优先保证响应速度,压缩率排在后面。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/645973.html





