在 IIS 上配置内容静态 gzip 压缩

将文本内容先在服务器上经过压缩再传递给客户端能够极大的减少数据传输所需要的流量,在某些情况下需要传输的数据会减少约 70%,这对站长的钱包和用户正在见底的手机流量都很有好处。唯一觉得不开心的可能是服务器的 CPU,因为在每次传输数据前都对数据进行压缩会给服务器的 CPU 造成一定的压力,为了解决这一问题,我们可以将站点的静态资源进行预压缩,这样在用户请求资源时,我们就不用先压缩这些文件,而只需要将提前压缩好的资源传给用户就好,有些前端框架(比如 Angular)甚至会直接提供预压缩过的资源文件可以说是非常贴心了。

通常情况下 Nginx / Apache 都能比较好的处理预压缩的问题,但是轮到 IIS 的时候就比较难搞了:直至 IIS 10,微软也没有提供直接读取预压缩文件的功能,因此我们需要手写 web.config 文件来实现这一功能,本文将简要介绍如何在 IIS 下搞定这一需求。

For English version, please checkout my answer on StackOverflow.

基本思路

我们需要首先了解客户端请求一份 gzip 过的档案所需要的先决条件:

  • 客户端声明自己支持解码 gzip 过的文件(或者 Brotli),这主要通过客户端请求 Header 中的 Accept-Encoding 字段达成;
  • 服务器声明自己提供的文件是 gzip 过的文件,这主要通过服务器响应 Header 中的 Content-Encoding 字段达成;
  • 服务器响应内容的 MIME 必须为原始文件的 MIME 类型,比如对于一份 HTML 档案,它的 MIME 类型就必须为 text/html 而不能是 application/gzip
  • 服务器提供了经过 gzip 压缩算法压缩过的档案。

这四个条件必须被同时达成才可以完成一次对压缩内容的传输,其中的任何一个条件被违反都会出现响应的错误:

  • 如果你发送了一个没有被压缩过的文件,但是服务器响应 Header 中却包含 Content-Encoding: gzip 字段,则浏览器会直接报错;
  • 如果你发送了一个压缩过的内容却没有在 Header 中标示 Content-Encoding 字段,或者提供了错误的 MIME 类型,那么在浏览器上会打印出乱码而非你期望的文本文件或图片;

操作流程

为了成功传输一个经过了预压缩的文件,我们需要做这几件事情:

  • 重新定义特定扩展名文件的 MIME 类型:
1
2
3
4
5
6
7
8
<staticContent>
<remove fileExtension=".js.gz" />
<remove fileExtension=".html.gz" />
<!--...-->
<mimeMap fileExtension=".js.gz" mimeType="application/javascript" />
<mimeMap fileExtension=".html.gz" mimeType="text/html" />
<!--...-->
</staticContent>

(你可以在这里看到完整的 MIME 类型清单。)

  • 如果客户端声明自己接受 gzip 压缩过文件,同时服务器上也有预压缩过的文件,则在服务器端直接提供 gzip 压缩过的文件(不推荐用 302/303/307 响应转向,一方面看起来很蠢,另外一方面客户端还要再发起一次请求很影响页面加载速度):
1
2
3
4
5
6
7
8
9
10
<rules>
<rule name="Rewrite gzip file">
<match url="(.*)"/>
<conditions>
<add input="{HTTP_ACCEPT_ENCODING}" pattern="gzip" />
<add input="{REQUEST_FILENAME}.gz" matchType="IsFile" />
</conditions>
<action type="Rewrite" url="{R:1}.gz" />
</rule>
</rules>
  • 如果客户端声明自己接受 gzip 压缩过的文件,同时服务器上也有预压缩过的文件,则修改响应的 Header,声明提供压缩过的文件:
1
2
3
4
5
6
7
8
9
<outboundRules rewriteBeforeCache="true">
<rule name="Custom gzip file header">
<match serverVariable="RESPONSE_CONTENT_ENCODING" pattern=".*" />
<conditions>
<add input="{REQUEST_URI}" pattern="\.gz$" />
</conditions>
<action type="Rewrite" value="gzip"/>
</rule>
</outboundRules>

完整范例

完整的 web.config 长这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<staticContent>
<remove fileExtension=".js.gz" />
<remove fileExtension=".css.gz" />
<remove fileExtension=".png.gz" />
<remove fileExtension=".jpg.gz" />
<remove fileExtension=".gif.gz" />
<remove fileExtension=".svg.gz" />
<remove fileExtension=".html.gz" />
<remove fileExtension=".json.gz" />
<mimeMap fileExtension=".js.gz" mimeType="application/javascript" />
<mimeMap fileExtension=".css.gz" mimeType="text/css" />
<mimeMap fileExtension=".png.gz" mimeType="image/png" />
<mimeMap fileExtension=".jpg.gz" mimeType="image/jpeg" />
<mimeMap fileExtension=".gif.gz" mimeType="image/gif" />
<mimeMap fileExtension=".svg.gz" mimeType="image/svg+xml" />
<mimeMap fileExtension=".html.gz" mimeType="text/html" />
<mimeMap fileExtension=".json.gz" mimeType="application/json" />
</staticContent>

<rewrite>
<outboundRules rewriteBeforeCache="true">
<rule name="Custom gzip file header">
<match serverVariable="RESPONSE_CONTENT_ENCODING" pattern=".*" />
<conditions>
<add input="{REQUEST_URI}" pattern="\.gz$" />
</conditions>
<action type="Rewrite" value="gzip"/>
</rule>
</outboundRules>

<rules>
<rule name="Rewrite gzip file">
<match url="(.*)"/>
<conditions>
<add input="{HTTP_ACCEPT_ENCODING}" pattern="gzip" />
<add input="{REQUEST_FILENAME}.gz" matchType="IsFile" />
</conditions>
<action type="Rewrite" url="{R:1}.gz" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>

以上就是本次介绍的全部内容,IIS 7 与 IIS 10 均尝试有效,如有问题可以邮件或电报交流,莉莉爱你 (((o(*゚▽゚*)o))) ♥~

Comments