使用ContentVersionStrategy的Spring Boot缓存禁止使用gzip进行资源压缩



我们有一个带有Thymelaf的Spring Boot web应用程序。在HTML模板中,我们引用了一些静态资源,例如/src/main/resources/static/js/main.js<script defer th:src="@{/js/main.js}"></script>

为了允许浏览器缓存我们网站多次访问的静态资源,我们启用了内容版本控制:

spring.resources:
chain:
strategy.content:
enabled: true
paths: /**
cache.cachecontrol.max-age: 365d

这一切都很好,我们得到了将MD5哈希附加到文件名(例如/main-d9f17fd70ee583fef4acf26dd331b8ab.js(的资源。

为了进一步减少流量,我们现在希望使用gzip:启用资源压缩

server:
compression:
enabled: true
mime-types: application/javascript,and-some-others
min-response-size: 1024

当请求具有标头Accept-Encoding='gzip'的(版本化的(资源时,我们不会得到具有Content-Encoding='gzip'的响应。因此,资源压缩似乎不能与内容版本控制结合使用。

如果我们禁用内容版本控制,资源压缩就可以正常工作:为(现在没有版本控制的(资源设置Content-Encoding='gzip'标头。

因此,我们深入研究了Spring的内部,发现了以下内容:

  1. org.springframework.web.servlet.resource.VersionResourceResolver#getResponseHeaders始终设置(强(ETag标头:
public HttpHeaders getResponseHeaders() {
HttpHeaders headers = (this.original instanceof HttpResource ?
((HttpResource) this.original).getResponseHeaders() : new HttpHeaders());
headers.setETag(""" + this.version + """);
return headers;
}
  1. org.apache.coyote.CompressionConfig#useCompression禁用压缩,如果存在强ETag:
public boolean useCompression(Request request, Response response) {
...
if (noCompressionStrongETag) {
String eTag = responseHeaders.getHeader("ETag");
if (eTag != null && !eTag.trim().startsWith("W/")) {
// Has an ETag that doesn't start with "W/..." so it must be a
// strong ETag
return false;
}
}
...
}

您可以将noCompressionStrongETag设置为false,但这是不推荐使用的,并且将随Tomcat 10…一起删除

为了证明这个问题,我在Github中创建了一个示例项目,其中有三个通过的测试和一个失败的测试,显示了我们的期望没有得到满足的地方。。。

你知道如何解决这个矛盾吗?我们是不是做错了什么?

现在在Spring的Github存储库中跟踪该问题。目前的想法是在Spring的VersionResourceResolver中从强ETag切换到弱ETag。

更新:该问题通过Spring Boot 2.4解决,它与Spring 5.3一起提供。包含修复。

一位贡献者在问题中强调,当前行为链接到Tomcat中的一个修复程序,该修复程序在此处解决。在那里,得出的结论是,响应压缩违反了强ETag的含义,因此是一个错误。因此,Tomcat只在不存在或只存在弱ETag标头的情况下应用压缩(这与我在SO问题中描述的观察结果一致(。

最新更新