我们有一个Thymeleaf的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的内部,发现了以下内容:
org.springframework.web.servlet.resource.VersionResourceResolver#getResponseHeaders
始终设置(strong)ETag标头:public HttpHeaders getResponseHeaders() {
HttpHeaders headers = (this.original instanceof HttpResource ?
((HttpResource) this.original).getResponseHeaders() : new HttpHeaders());
headers.setETag("\"" + this.version + "\"");
return headers;
}
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中删除...
为了演示该问题,我创建了一个example project in Github,其中包含3个通过测试和1个失败测试,这些测试表明无法满足我们的期望...
您有解决该矛盾的想法吗?我们在做严重错误的事情吗?
现在在Spring's Github repository中跟踪了该问题。当前的想法是在Spring的VersionResourceResolver
中从强ETag切换到弱ETag。问题解决后,我将更新此答案。