# HTTP/2, compression and caching on IIS and Nginx

> Enable HTTP/2 over TLS, compress text correctly, set cache headers, and avoid double compression or caching secrets.

**Tested on**

- Build: 26100.33650
- Edition: Standard / Datacenter
- Architecture: x64
- Evidence: E1
- Verified: 2026-10-29

## The short answer

- **HTTP/2** requires TLS in browsers; IIS enables it by default on modern Windows with HTTPS.
- **Compression** is for text (HTML/CSS/JS/JSON/SVG), not images/video/zip.
- **Caching** is a policy decision per path; do not cache authenticated HTML.

## HTTP/2

IIS: HTTPS binding is the main requirement; HTTP/2 is negotiated via ALPN. Nginx:

```nginx
listen 443 ssl;
http2 on;
```

Verify:

```powershell
curl.exe -I --http2 https://lab.example/
```

The status line should show `HTTP/2`. If it shows `HTTP/1.1`, check TLS/ALPN and whether a proxy is downgrading.

HTTP/2 is not HTTP/3; see the HTTP/3 page for QUIC.

## Compression

IIS `web.config`:

```xml
<system.webServer>
  <httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
    <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
    <dynamicTypes>
      <add mimeType="text/*" enabled="true" />
      <add mimeType="application/json" enabled="true" />
      <add mimeType="application/javascript" enabled="true" />
      <add mimeType="*/*" enabled="false" />
    </dynamicTypes>
    <staticTypes>
      <add mimeType="text/*" enabled="true" />
      <add mimeType="application/javascript" enabled="true" />
      <add mimeType="image/svg+xml" enabled="true" />
      <add mimeType="*/*" enabled="false" />
    </staticTypes>
  </httpCompression>
</system.webServer>
```

Nginx:

```nginx
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_min_length 1024;
gzip_vary on;
```

Do not gzip already-compressed files; do not double-compress at the proxy and origin.

## Caching

```xml
<location path="assets">
  <system.webServer>
    <staticContent>
      <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
    </staticContent>
  </system.webServer>
</location>
```

For hashed assets, a long `max-age` + `immutable` is safe. For HTML, use `no-cache` or a short TTL so deploys are visible.

> **Caution: Never cache authenticated HTML publicly**
>
> A shared cache must not store a page containing another user’s data. Mark `Cache-Control: private, no-store` for account pages; test through the CDN/proxy, not only the origin.

## Verify

```powershell
curl.exe -I --compressed https://lab.example/assets/app.js
curl.exe -I https://lab.example/account
```

Check `content-encoding`, `cache-control`, `vary`, and that the account page is not publicly cacheable.

## Lab boundary

E1. The lab served IIS/Nginx over HTTP and did not validate HTTP/2, compression or CDN caching. The config is the verification plan for Server 2025.

## Sources

- [Microsoft Learn — HTTP/2 on IIS](https://learn.microsoft.com/en-us/iis/get-started/whats-new-in-iis-10/http2-on-iis)
- [Microsoft Learn — IIS compression](https://learn.microsoft.com/en-us/iis/configuration/system.webserver/httpcompression/)
- [nginx — gzip](https://nginx.org/en/docs/http/ngx_http_gzip_module.html)

---

Source: https://winbook.fyi/en/server/tls/http2/
