Cloudflare のコンテンツ圧縮に関わる挙動を確認する

# コンテンツ圧縮に関わる挙動
以下のような使用のため、「Eyeball <--> Cloudflare」と「Cloudflare <--> Origin」を基本的には独立した通信の仕組みとして考えることができます。

- [How does Cloudflare compress content? · Cloudflare Speed docs](https://developers.cloudflare.com/speed/optimization/content/brotli/content-compression/)
> Cloudflare will take into consideration the Accept-Encoding header value in website visitors’ requests when sending responses to those visitors. However, when requesting content from your origin server, Cloudflare will send a different Accept-Encoding header, supporting GZIP and Brotli compression.
> Cloudflareは、ウェブサイトの訪問者のリクエストに含まれるAccept-Encodingヘッダーの値を、それらの訪問者にレスポンスを送信する際に考慮します。しかし、オリジンサーバーからコンテンツをリクエストする場合、CloudflareはGZIPおよびBrotli圧縮をサポートする別のAccept-Encodingヘッダーを送信します。

- [最大レベル11まで提供：Brotli圧縮とCompression Rulesの導入](https://blog.cloudflare.com/ja-jp/this-is-brotli-from-origin-ja-jp)

> CloudflareはBrotliをエンドツーエンドで完全にサポートするようになったため、お客様は更新されたaccept-encodingヘッダーがオリジンに到着するのを確認できるようになります。利用可能になると、お客様は、最大レベルの11まで、大きく圧縮されたBrotliファイルを直接転送、キャッシュ、提供することができます。これは、遅延と帯域幅の消費を削減するのに役立ちます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/cc8d31c3-8e1e-adaf-04e4-6da12a1d36f5.png)

# Eyeball <--> Cloudflare 

- [Compression between Cloudflare and website visitors · Cloudflare Speed docs](https://developers.cloudflare.com/speed/optimization/content/brotli/content-compression/#compression-between-cloudflare-and-website-visitors)

```mermaid:
flowchart LR
    Eyeball[Eyeball] --> |Request|Br{Accept-Encoding Header <br/>contains br}
    subgraph Cloudflare
        Br -->|Yes| BrRes[Brotli-encoded response]
        Br -->|No | GZIP{Accept-Encoding Header <br/>contains gzip}
        GZIP -->|Yes| GzipRes[GZIP-encoded response]
        GZIP --->|No| NoRes[No compression]
    end
```

## `Accept-Encoding` ヘッダに `br` が含まれる場合

```shell:
% curl -sIXGET https://www.cloudflare.com -H "Accept-Encoding: br" | grep -e content-encoding -e cf-cache-status
cf-cache-status: HIT
content-encoding: br

% curl -sIXGET https://www.cloudflare.com -H "Accept-Encoding: gzip, br" | grep -e content-encoding -e cf-cache-status
cf-cache-status: HIT
content-encoding: br
```

## `Accept-Encoding` ヘッダに `br` がなく `gzip` がある場合

```shell:
% curl -sIXGET https://www.cloudflare.com -H "Accept-Encoding: gzip" | grep -e content-encoding -e cf-cache-status
cf-cache-status: HIT
content-encoding: gzip
```

## `Accept-Encoding` ヘッダに `br` も `gzip` もない場合

```shell:
% curl -sIXGET https://www.cloudflare.com | grep -e content-encoding -e cf-cache-status 
cf-cache-status: HIT

```

# Cloudflare <--> Origin
2024年3月下旬にオリジンへのリクエストに対して Brotli 圧縮が有効になりました。

- [Content compression from origin servers to the Cloudflare network · Cloudflare Speed docs](https://developers.cloudflare.com/speed/optimization/content/brotli/content-compression/#content-compression-from-origin-servers-to-the-cloudflare-network)
- [Upcoming Change To Accept Encoding Header - What's New - Cloudflare Community](https://community.cloudflare.com/t/upcoming-change-to-accept-encoding-header/517115)

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">It took a little longer than expected, with more edge cases than we anticipated (i.e. Fake-Brotli now lovingly known as Frotli), but <a href="https://twitter.com/Cloudflare?ref_src=twsrc%5Etfw">@Cloudflare</a> now supports Brotli compression from the origin for all our customers! more details here: <a href="https://t.co/cG4CU9YIll">https://t.co/cG4CU9YIll</a> 🚀 <a href="https://twitter.com/hashtag/WebPerformance?src=hash&amp;ref_src=twsrc%5Etfw">#WebPerformance</a></p>&mdash; Matt Bullock (@mibullock) <a href="https://twitter.com/mibullock/status/1772299955765408012?ref_src=twsrc%5Etfw">March 25, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

```mermaid:
flowchart LR
    Request[Cloudflare] --> |Request<br/>Accept-Encoding: gzip, br|Br{Origin can respond using <br/>Brotli compression}
    subgraph Origin
    Br -->|Yes| BrRes[Brotli-encoded response]
    Br -->|No | GZIP{Origin can respond using <br/>Gzip compression}
    GZIP -->|Yes| GzipRes[GZIP-encoded response]
    GZIP --->|No| NoRes[No compression]
    end
```

## 参考：オリジン設定
`LogFormat` に `\"%{Content-Encoding}o\"` を追加することで、オリジンからのレスポンスが `gzip` か `br` かを確認できます。

また、`BrotliCompressionQuality` を `11` にすることで最大の圧縮効果が得られます。

```shell:
sudo dnf install brotli -y
```

```conf:/etc/httpd/conf.d/ssl.conf
<VirtualHost *:443>
...
LogFormat "%h:443 - - %t \"%r\" %>s %b %{Host}i%U%q \"%{Referer}i\" \"%{User-Agent}i\" %D %{HTTPS}e \"%{Range}i\" \"%{Content-Encoding}o\" "
TransferLog logs/ssl_access_log
...
# Dynamic Brotli:
<IfModule mod_brotli.c>
    <IfModule mod_filter.c>
        # Set the compression quality level, lower will be faster but worse compression (0-11, default 5)
        BrotliCompressionQuality 11
        
        AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/xml text/css
        AddOutputFilterByType BROTLI_COMPRESS application/x-javascript application/javascript application/ecmascript text/javascript application/javascript application/json
        AddOutputFilterByType BROTLI_COMPRESS application/rss+xml
        AddOutputFilterByType BROTLI_COMPRESS application/xml
        AddOutputFilterByType BROTLI_COMPRESS image/svg+xml
        AddOutputFilterByType BROTLI_COMPRESS application/x-font-ttf application/vnd.ms-fontobject image/x-icon
    </IfModule>
</IfModule>
 
# Dynamic gzip:
<IfModule mod_deflate.c>
    <IfModule mod_filter.c>
        AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
        AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript text/javascript application/javascript application/json
        AddOutputFilterByType DEFLATE application/rss+xml
        AddOutputFilterByType DEFLATE application/xml
        AddOutputFilterByType DEFLATE image/svg+xml
        AddOutputFilterByType DEFLATE application/x-font-ttf application/vnd.ms-fontobject image/x-icon
    </IfModule>
</IfModule>
...
</VirtualHost> 
```

## オリジンが Brotli 圧縮に対応可能な場合
オリジンから `Content-Encoding: br` でレスポンスされることが確認できます。

```conf:/etc/httpd/logs/ssl_access_log
172.70.123.12:443 - - [26/Mar/2024:16:23:30 +0000] "GET / HTTP/1.1" 200 1923 example.com/index.html "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" 9973 on "-" "br" 
```

## オリジンが Gzip 圧縮のみ対応可能な場合
オリジンから `Content-Encoding: gzip` でレスポンスされることが確認できます。

```conf:/etc/httpd/logs/ssl_access_log
172.68.118.96:443 - - [26/Mar/2024:16:17:47 +0000] "GET / HTTP/1.1" 200 2137 example.com/index.html "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" 6686 on "-" "gzip" 
```

## どちらの圧縮方式にも非対応な場合
オリジンからのレスポンスに `Content-Encoding` ヘッダはなく、圧縮されません。

```conf:/etc/httpd/logs/ssl_access_log
172.70.222.209:443 - - [26/Mar/2024:16:25:46 +0000] "GET / HTTP/1.1" 200 5187 example.com/index.html "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" 6638 on "-" "-" 
```

# End-to-End compression

オリジンからの圧縮されたレスポンスに対して、Cloudflare 側で「解凍＆再圧縮」の処理を発生させないためには以下のいくつかの機能が無効化されている必要があります。

こちらもご留意ください。

- [​​Notes about end-to-end compression · Cloudflare Speed docs](https://developers.cloudflare.com/speed/optimization/content/brotli/content-compression/#notes-about-end-to-end-compression)

> 同じ圧縮アルゴリズムをエンドツーエンド（オリジンサーバーとCloudflare間、Cloudflareグローバルネットワークとウェブサイト訪問者間）で使用している場合でも、リクエストに対して以下のオプションのいずれかを有効にすると、Cloudflareはレスポンスを解凍し、再度圧縮する必要があります：
>
> - Email Address Obfuscation
> - Rocket Loader
> - Server Side Excludes (SSE)
> - Mirage
> - HTML Minification (you can minify JavaScript and CSS without any impact)
> - Automatic HTTPS Rewrites
> - Polish
> 
> 特定のURIパスに対してこれらの機能を無効にするには、設定ルールを作成します。
> 
>さらに、Cloudflare FontsもCloudflareによるレスポンスの解凍と再圧縮が必要であり、現時点ではRulesで無効にすることはできません。

# 参考
次の圧縮方式としては zStandard (`zstd`) が出てきているので、Cloudflare でも今後の対応が期待されます。

- [Choosing Between gzip, Brotli and zStandard Compression | Paul Calvano](https://paulcalvano.com/2024-03-19-choosing-between-gzip-brotli-and-zstandard-compression/)