Files
cpp-httplib/docs-src/pages/ja/cookbook/s08-compress-response.md
T
yhirose fc37d3eae1 Add Server::set_compress() to turn off handler response compression
A server built with compression support compressed every handler
response the client accepted, with no way to turn it off. Over HTTPS
the length of a compressed response can give away a secret in it when
the same response also carries text an attacker chooses (BREACH).
set_compress(false) sends handler responses, buffered or streamed, as
they are; compression stays on by default, and static file compression
keeps its own setting. The README and the cookbook describe the switch
and when to use it.
2026-10-21 23:34:48 -04:00

87 lines
5.2 KiB
Markdown

---
title: "S08. レスポンスを圧縮して返す"
order: 27
status: "draft"
---
cpp-httplibは、クライアントが`Accept-Encoding`で対応を表明していれば、レスポンスボディを自動で圧縮してくれます。ハンドラ側で特別なことをする必要はありません。対応しているのはgzip、Brotli、Zstdです。
## ビルド時の準備
圧縮機能を使うには、`httplib.h`をインクルードする前に対応するマクロを定義しておきます。
```cpp
#define CPPHTTPLIB_ZLIB_SUPPORT // gzip
#define CPPHTTPLIB_BROTLI_SUPPORT // brotli
#define CPPHTTPLIB_ZSTD_SUPPORT // zstd
#include <httplib.h>
```
それぞれ`zlib`、`brotli`、`zstd`をリンクする必要があります。必要な圧縮方式だけ有効にすればOKです。
## 使い方
```cpp
svr.Get("/api/data", [](const httplib::Request &req, httplib::Response &res) {
std::string body = build_large_response();
res.set_content(body, "application/json");
});
```
これだけです。クライアントが`Accept-Encoding: gzip`を送ってきていれば、cpp-httplibが自動でgzip圧縮して返します。レスポンスには`Content-Encoding: gzip`と`Vary: Accept-Encoding`が自動で付きます。
## 圧縮の優先順位
クライアントが複数の方式を受け入れる場合は、`Accept-Encoding`のq値がいちばん高い方式で圧縮します。q値が同じときは、ビルドで有効な方式の中からBrotli → gzip → Zstdの順に選びます。
## ストリーミングレスポンスも圧縮される
`set_chunked_content_provider()`で返すストリーミングレスポンスも、同じように自動で圧縮されます。
```cpp
svr.Get("/events", [](const httplib::Request &req, httplib::Response &res) {
res.set_chunked_content_provider(
"text/plain",
[](size_t offset, httplib::DataSink &sink) {
// ...
});
});
```
## 圧縮を止める
```cpp
svr.set_compress(false);
```
ハンドラのレスポンスは、通常のボディもストリーミングも、そのまま送られます。HTTPSでは、攻撃者が選べる文字列とCSRFトークンなどの秘密が同じレスポンスに載ると、圧縮後の長さから秘密が漏れることがあります(BREACH攻撃)。静的ファイルの圧縮は別に設定します。
## 静的ファイルは明示的に有効にする
`set_mount_point()`や`Response::set_file_content()`でファイルをそのまま返す場合、デフォルトでは圧縮されません。有効にするには次を呼びます。
```cpp
svr.set_static_file_compression(true);
```
圧縮の対象になるのは一定のサイズ範囲に収まるファイルだけで、上下どちらの境界も変更できます。
```cpp
svr.set_static_file_compression_min_length(512);
svr.set_static_file_compression_max_length(1024 * 1024);
```
下限のデフォルトは1400バイトです。1500バイトのMTUに収まるレスポンスは、小さくしたところで到達が速くなるわけではありません。さらに数バイトのファイルは、gzipのヘッダとトレーラがdeflateの削減分を上回るため、かえって大きくなって返ります。
上限のデフォルトは4MBで、こちらは理由が違います。リクエストのたびに圧縮が走り、圧縮後のバイト列はレスポンスを書き終えるまでメモリに載るため、ピーク時のコストが同時処理中のリクエスト数に比例するからです。つまり1リクエストあたりのコストを抑えるための値であって、大きいファイルは圧縮しても無駄だという意味ではありません。配信するファイルが分かっていてトラフィックがそれほど多くないなら、引き上げて構いません。
どちらの境界も`0`で無効にできます。コンパイル時のデフォルトは`CPPHTTPLIB_STATIC_FILE_COMPRESSION_MIN_LENGTH`と`CPPHTTPLIB_STATIC_FILE_COMPRESSION_MAX_LENGTH`で決まります。
圧縮しても`Content-Length`は付いたままなので、`HEAD`は`GET`と同じサイズを返します。細かい点が2つあります。Rangeリクエストには圧縮前のデータから該当範囲を切り出して返します。また、`ETag`には`W/"...-gzip"`のように、使った圧縮方式が入ります。
なお`set_content_provider()`で登録したコンテンツプロバイダは対象外です。圧縮器を通すと、内部バッファが埋まるまで書き込みが送出されず、ボディを少しずつ生成するプロバイダが止まってしまうためです。生成したボディを圧縮したい場合は`set_chunked_content_provider()`を使ってください。
> **Note:** サイズ範囲が効くのは静的ファイルだけです。`set_content()`に渡したボディは、圧縮対象のMIMEタイプでクライアントが受け入れていれば、大きさによらず圧縮されます。数バイトのレスポンスはgzipのヘッダ分だけかえって大きくなるので、避けたい場合はハンドラ側で判断してください。
> クライアント側の挙動は[C15. 圧縮を有効にする](../c15-compression)を参照してください。