【问题标题】:Setup IIS10 to serve pre-compressed files设置 IIS10 以提供预压缩文件
【发布时间】:2018-02-20 16:20:36
【问题描述】:

我的 angular2 项目为我的网络应用程序构建了预压缩的 gzip 文件,但我的 IIS 只提供普通的“.js”文件,而不是压缩的“.gzip”文件。我的浏览器愿意接受 gzip。

IIS 允许 gzip 响应的正确设置是什么?

我已经搜索了 google/SO/SU,但只找到了针对非“预压缩”内容的解决方案。

【问题讨论】:

    标签: angular iis gzip http-compression


    【解决方案1】:

    更简洁优雅的解决方案:

    注意:文件扩展名.gzip 看起来很奇怪,一般情况下,我们将压缩后的文件命名为.gz,所以在这个例子中,我们使用.gz 而不是.gzip,如果你坚持使用.gzip,只需替换以下配置文件中的所有扩展名即可。


    代码优先,这就是我们所需要的web.config

    <?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>
    

    而且,它的工作原理如下:

    为了实现一次成功的gzip数据传输,我们需要:

    • 客户端接受压缩数据,Accept-Encoding
    • 带有Content-Encoding 标头的响应
    • 正确的 MIME 类型,与原始文件一样,但不是 application/gzip
    • Gzip 文件

    四个条件必须同时满足。

    如果用Content-Encoding: gzip发送未压缩文件,浏览器会返回错误;

    如果您发送的压缩文件没有 Content-Encoding 标头或 MIME 类型不匹配,则页面可能会返回一些 Zenith Star 的文本。

    所以我们要做的是:

    • 重新定义每种类型的 gzip 文件的 MIME
    • 如果客户端接受 gzip 文件,则将响应文件重定向到 服务器端直接(不是 302/303/307 响应)
    • 重写Content-Encoding的响应头的头,仅当客户端发送头Accept-Encoding

    此解决方案适用于我的 IIS7,不确定它是否也适用于 IIS10。

    如果您遇到任何问题,请告诉我:D

    【讨论】:

    • 感谢您指出这一点。不幸的是,我无法让它在 IIS 10 中工作(你说它可能不会)。此外,我最终发现我们在这里查看的答案*.com/questions/45199213/… 并不适用于所有浏览器。 FireFox(例如)不会加载任何带有错误 SyntaxError: 非法字符的 .js 文件。不过,我无法确认或否认您的解决方案是否存在同样的问题。
    • @mikeo hmmmmmm,我刚刚在我的 Windows 10 上尝试了这个配置,使用 IIS 10,它运行良好......
    • 它提供预压缩文件?不是动态压缩的?那是我无法让它去的地方。它提供压缩文件,但我们是动态压缩的……显示文件大小比我的预压缩文件大一些。如果是这样,我会再试一次,让你知道。
    • @mikeo 尝试先禁用动态 gzip,如果它在此之后有效,我们可能会想出另一个解决方案。
    • 是的,当我删除动态时,我两者都做了(在不同的时间进行故障排除),它只提供比预压缩或动态压缩大得多的完整文件。奇怪的是,其他答案设置(与您的类似但不如您的强大)在 IIS10 中有效(在除 Firefox 之外的所有内容中)。所以,我不确定为什么这也不起作用。
    【解决方案2】:

    经过长时间的搜索,我找到了 URL-Rewrite 的解决方法。

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <system.webServer>
            <httpProtocol>
                <customHeaders>
                    <remove name="X-Powered-By" />
                </customHeaders>
            </httpProtocol>
            <rewrite>
                <rules>
                    <clear />
                    <rule name="Https redirect" stopProcessing="true">
                        <match url="(.*)" />
                        <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                            <add input="{HTTP_HOST}" pattern="^domain.com$" />
                            <add input="{HTTPS}" pattern="^OFF$" />
                        </conditions>
                        <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" />
                    </rule>
                    <rule name="LetsEncrypt">
                        <match url=".well-known/acme-challenge/*" />
                        <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                        <action type="None" />
                    </rule>
                    <rule name="Angular Routes" stopProcessing="true">
                        <match url="(.*)" />
                        <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                            <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
                        </conditions>
                        <action type="Rewrite" url="/" />
                    </rule>
                    <rule name="br_rewrite" enabled="true" stopProcessing="true">
                        <match url="(.*).(js$|svg|css)" />
                        <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                            <add input="{HTTP_ACCEPT_ENCODING}" pattern="br" />
                            <add input="{REQUEST_FILENAME}" matchType="IsFile" />
                        </conditions>
                        <action type="Rewrite" url="{R:1}.{R:2}.br" logRewrittenUrl="true" />
                    </rule>
                    <rule name="gzip_rewrite" enabled="true" stopProcessing="true">
                        <match url="(.*).(js$|svg|css)" />
                        <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                            <add input="{HTTP_ACCEPT_ENCODING}" pattern="gzip" />
                            <add input="{REQUEST_FILENAME}" matchType="IsFile" />
                        </conditions>
                        <action type="Rewrite" url="{R:1}.{R:2}.gz" logRewrittenUrl="true" />
                    </rule>
                </rules>
                <outboundRules rewriteBeforeCache="true">
                    <rule name="Remove Server header" enabled="true">
                        <match serverVariable="RESPONSE_Server" pattern=".+" />
                        <action type="Rewrite" value="" />
                    </rule>
                    <rule name="Rewrite content-encoding header gzip" preCondition="IsGZ" enabled="true" stopProcessing="false">
                        <match serverVariable="RESPONSE_CONTENT_ENCODING" pattern=".*" />
                        <action type="Rewrite" value="gzip" />
                    </rule>
                    <rule name="Rewrite content-encoding header br" preCondition="IsBR" enabled="true" stopProcessing="false">
                        <match serverVariable="RESPONSE_CONTENT_ENCODING" pattern=".*" />
                        <action type="Rewrite" value="br" />
                    </rule>
                    <rule name="css content type" preCondition="IsCSS" enabled="true" stopProcessing="false">
                        <match serverVariable="RESPONSE_CONTENT_TYPE" pattern="(.*)" />
                        <action type="Rewrite" value="text/css" />
                    </rule>
                    <rule name="js content type" preCondition="IsJS" enabled="true" stopProcessing="false">
                        <match serverVariable="RESPONSE_CONTENT_TYPE" pattern="(.*)" />
                        <action type="Rewrite" value="application/javascript" />
                    </rule>
                    <rule name="svg content type" preCondition="IsSVG" enabled="true" stopProcessing="false">
                        <match serverVariable="RESPONSE_CONTENT_TYPE" pattern="(.*)" />
                        <action type="Rewrite" value="image/svg+xml" />
                    </rule>
                    <preConditions>
                        <preCondition name="IsGZ">
                            <add input="{URL}" pattern="\.gz$" />
                        </preCondition>
                        <preCondition name="IsBR">
                            <add input="{URL}" pattern="\.br$" />
                        </preCondition>
                        <preCondition name="IsCSS">
                            <add input="{URL}" pattern="css" />
                        </preCondition>
                        <preCondition name="IsJS">
                            <add input="{URL}" pattern="js" />
                        </preCondition>
                        <preCondition name="IsSVG">
                            <add input="{URL}" pattern="svg" />
                        </preCondition>
                    </preConditions>
                </outboundRules>
            </rewrite>
            <urlCompression doStaticCompression="true" doDynamicCompression="false" />
            <httpCompression sendCacheHeaders="false" />
            <staticContent>
                <mimeMap fileExtension=".br" mimeType="application/brotli" />
                <clientCache cacheControlMode="UseMaxAge" />
            </staticContent>
        </system.webServer>
    </configuration>
    

    它成功处理了对预构建 Angular 文件(JS、CSS、SVG)的 BR 和 GZIP 请求。

    我希望这对其他人有所帮助。如果您知道更好的解决方案,请告诉我。

    【讨论】:

    • 使用这个解决方法,我可以看到 Chrome 现在使用 brotli 文件。但是当我删除 brotli 的规则并保留 gzip 时,压缩在 IIS 10 中不起作用。@Alex
    • 我不是 IIS 专家,所以我的解决方案是错误尝试(很可能很容易出错)。您是否尝试过 @Losses Don 的解决方案?他提供了一个更简洁的解决方案,如果您只需要 gzip,它可能不会出现问题。
    • 我做到了。使用它似乎提供gz 文件(我理解大小),但由于缺少浏览器无法解码的标题content-encoding: gzip 而发生语法错误。
    • 我认为需要启用 SSL 才能获取预压缩文件,即使服务器配置如前所述。