CDN页面优化不生效排查遇到的坑-阿里云开发者社区

开发者社区> 云服务技术课堂> 正文

CDN页面优化不生效排查遇到的坑

简介: 如果源站响应给CDN的数据是Gzip压缩以后的数据会导致CDN的页面优化不生效。本文详细讲述了问题的原因以及排查过程,并讲述了Nginx关于Gzip的压缩配置,同时介绍了CDN作为代理服务,引入了Via header以后对Nginx服务器的影响。

背景描述

阿里云CDN提供了页面优化功能,当开启页面优化功能时,CDN可以自动清除HTML页面冗余的注释和重复的空白符,缩小文件体积,提升页面可阅读性。本案例遇到的一个问题是,按照文档开启了CDN的页面优化功能,但是访问HTML页面实际并没有优化效果。

Gzip压缩引发

在排查测试的过程中,发现直接用curl URL的方式查看响应结果,是已经被页面优化了,但是直接在浏览器上访问HTML却没有被优化。进一步对比curl请求以及浏览器Network下的Request Header以及Response Header,发现浏览器的Request Header带了Accept-Encoding: gzip, deflate,Response Header返回了Content-Encoding: gzip,如下图所示
image.png
image.png

我们知道HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术,大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。从测试来看,浏览器请求头带了Accept-Encoding: gzip表示浏览器支持解码gzip压缩后的文件,如果服务器支持gzip压缩,那么在收到这个请求头以后,就会返回gzip压缩以后的文件,在Response Header里以Content-Encoding: gzip的形式体现。直接用如下curl命令带Accept-Encoding请求头测试,发现返回结果也是带了gzip的,且页面也是没有优化的。

curl -I 'http://测试URL' -H 'Accept-Encoding: gzip'

综上可以暂时得出结论,返回结果带了Accept-Encoding: gzip以后,CDN的页面压缩不生效,返回结果不带Accept-Encoding: gzip,CDN的页面压缩生效。

Gzip压缩是在哪里发生的

由于请求的链路是:客户端Client -->CDN -->源站
这个Gzip压缩,可能是CDN产生的,也可能是源站产生的。这个比较容易验证,直接用curl命令绑定到源站IP去测试即可得出结论

curl -I 'http://测试URL' -H 'Accept-Encoding: gzip' -x '源站IP:80'

测试来看,返回结果果然带了Accept-Encoding: gzip,说明是源站gzip压缩导致的。产生这个现象的原因逐渐浮出水面,整个过程如下:

当用户在浏览器发起请求时,浏览器默认带了Accept-Encoding: gzip这个请求头,CDN作为一个代理服务器,在回源请求源服务器的时候转发了这个来自真实客户端(浏览器)的请求头,源服务器由于开了Gzip压缩,因此在收到这个请求头以后返回了Gzip压缩以后的内容给CDN,由于CDN不具备Gunzip功能,因此无法对Gzip压缩以后的内容去做页面优化,因此导致了页面优化功能不生效。

Gzip配置参数

以上基本定位问题,但是为了更明确,我搭建了一个基于Nginx的Web服务器用做CDN的源站,并在Nginx的配置文件nginx.conf里开启了Gzip压缩,配置如下图
image.png
但是测试发现,通过CDN访问,并且发了Accept-Encoding: gzip请求头以后,CDN依然可以完成页面压缩,这就比较奇怪。为了定位问题,直接在Web服务器上抓了包,看一下CDN和Web服务器的交互请求,发现一个很奇怪的现象:CDN请求Web服务器的时候转发了Accept-Encoding: gzip,但是Web服务器并没有响应Content-Encoding: gzip,报文如下图:
image.png

根据这个现象,去查了一下Nginx官网对于ngx_http_gzip_module模块的配置说明,可以看到该模块有如下配置参数
image.png

其中有一个gzip_proxied参数引起了注意。这个参数的含义,可以解释如下
语法: gzip_proxied [off|expired|no-cache|no-store|private|no_last_modified|no_etag|auth|any] …
默认值: gzip_proxied off
作用域: http, server, location
Nginx作为反向代理的时候启用,开启或者关闭后端服务器返回的结果,匹配的前提是后端服务器必须要返回包含”Via”的 header头。

off – 对于所有来自代理服务器的请求,都关闭压缩
expired – 如果响应header头中包含 “Expires” 头信息则启用压缩
no-cache – 如果响应header头中包含 “Cache-Control:no-cache” 头信息则启用压缩
no-store – 如果响应header头中包含 “Cache-Control:no-store” 头信息则启用压缩
private – 如果响应header头中包含 “Cache-Control:private” 头信息则启用压缩
no_last_modified – 如果响应header头中不包含 “Last-Modified” 头信息则启用压缩
no_etag – 如果响应header头中不包含 “ETag” 头信息则启用压缩
auth – 如果响应header头中包含 “Authorization” 头信息则启用压缩
any – 无条件启用压缩,也就是对任何来自代理服务器的请求,都返回压缩的内容
image.png

由于Nginx配置里配置了 gzip_proxied expired no-cache no-store private auth
因此相当于启用了gzip_proxied参数,当Web服务器发现来自代理服务器的请求时(在这里就是来自CDN的请求),Web服务器会去校验gzip_proxied参数,当发现服务器的Response Header里没有返回Expires、"Cache-Control:no-cache"等类似响应头时,服务器就返回了不带Gzip压缩的数据。如果Gzip配置模块是按照如下配置的话,那么任何来自代理服务器的请求,服务器都会返回Gzip压缩的内容。

gzip_proxied  any

如何判断请求是来自代理服务器的

那么问题来了,服务器是如何判断这个请求是来自代理服务器的,而不是真实客户端呢。这里就涉及到Via这个HTTP Header了。关于Via的介绍,可以参考HTTP协议关于Via的文档。Via 是一个通用首部,是由代理服务器添加的,适用于正向和反向代理,在请求和响应首部中均可出现。这个消息首部可以用来追踪消息转发情况,防止循环请求,以及识别在请求或响应传递链中消息发送者对于协议的支持能力。在这里,CDN作为代理服务器,去请求源服务器的时候,请求头里会带上Via头(这点在上面的抓包截图里也可以看到),而服务器就是根据请求头里的Via得知该请求是来自上游代理服务器的。

HTTP服务器的问题是知道代理本身是否能够处理压缩响应。传入请求中的接受编码头(也就是Accept-Encoding: gzip)很可能是由原始客户机请求提供的,但这并不能表明它所经过的代理或网关的能力,也就是说,服务器并不知道上游代理服务器能否处理Gzip压缩以后的内容。因此,在此场景中,服务器采用最安全的选项,并选择不压缩它发回的响应,这也是合理的。关于Via这个Header对于Gzip压缩的影响,可以参考这篇Akamai的文章,有详细的介绍。

总结和解决方案

综上,该问题可以总结如下
(1)如果源站响应了Gzip压缩的内容,CDN会因为无法Gunzip导致页面优化功能不生效
(2)如果期望使用CDN的页面优化,那么需要确保源站服务器关闭Gzip压缩。如果源站服务器是Nginx,通过修改Nginx配置文件里ngx_http_gzip_module模块的gzip_proxied参数,设定来自代理服务器的请求,不返回Gzip压缩的内容来实现。
(3)另外还有一种实现方案,是可以在CDN层面配置删除Accept-Encoding这个回源HTTP请求头。
image.png

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:

云服务技术课堂,各类技术课程、最佳实践输出,来好好听课吧!

官方博客