🍼

Accept-Encoding と Content-Encoding を正しく理解する

に公開

はじめに

「圧縮して通信量を減らす」ことはもう当たり前。でもクライアントは何ができて、サーバは何を返しているのか?
ここを理解してないとトラブルの原因に。

この記事では、HTTP で使われる Accept-Encoding / Content-Encoding とは何か?
そして、クライアントやツールごとの挙動を実際に検証して整理します。

ゴール

  • 両者の役割と違いが直感で分かる
  • 圧縮問題が起きたときの調査方法が理解できる
  • fetch / curl / Bruno の違いを使い分けられる

仕組み

ヘッダ 誰が送る? 内容 一言まとめ
Accept-Encoding クライアント → サーバ 自分が 解凍できる圧縮方式のリスト 「こういう圧縮ならOK!」
Content-Encoding サーバ → クライアント 実際にサーバが 圧縮して返した方式 「gzip で返したよ!」

Accept-Encoding: の意味

例:
Accept-Encoding: gzip, deflate, br
=「gzip / deflate / br のどれでも解凍できるよ!」

サーバ側の Content-Encoding 決定ロジック

  1. Accept-Encoding のリストを見る
  2. サーバが対応している圧縮方式と照合
  3. 選ばれた方式で圧縮
  4. Content-Encoding を付与して返す

例:
Content-Encoding: gzip
=「gzipで圧縮したから解凍してね!」

圧縮方式の一覧とその意味

圧縮方式 何をするか / 特徴
gzip LZ77+Huffman コーディング (DEFLATE ベース) を使った汎用圧縮。テキストファイル(HTML, CSS, JS, JSON など)を効率よく圧縮可能で、歴史も長く、ほとんどすべてのブラウザ/サーバで広くサポートされている。(MDN Web Docs)
deflate zlib 構造 (RFC 1950) + DEFLATE アルゴリズム (RFC 1951) による圧縮。gzip に近いが、ヘッダー/フッターの扱いや互換性に差異があり、現在は gzip の方が一般的。(MDN Web Docs)
br (Brotli) より最新で高度な圧縮方式。LZ77 に加え、Huffman コーディングと第二次コンテキストモデリング + 定義済み 辞書 (common phrases, HTML/CSS/JS のよくあるパターンなど) を使う。gzip より高い圧縮率を出せることが多く、特に Web のテキスト資産 (HTML, CSS, JS) で効果大。(ウィキペディア)
identity 圧縮しない — 生 (raw) のまま送る/受け取ることを意味する「圧縮なし」の指定。圧縮を望まない、あるいは圧縮の効果が低いと判断された場合に使われる。(MDN Web Docs)
その他(compress など) 昔の圧縮方式。現在は非推奨、不安定、互換性問題のためあまり使われない。(MDN Web Docs)

圧縮の目的

  • 通信量(バイト量)の削減 → 帯域使用量を節約
  • 通信速度の改善 → 転送データが小さければ、ネットワーク越しの遅延や待機時間が減る
  • ユーザー体験の向上 → ページや API レスポンスの応答/ロードが速くなる

圧縮方式ごとのメリット/トレードオフ

圧縮方式 長所 短所・注意点
br (Brotli) 非常に高い圧縮率 — ページ/レスポンスサイズを最大限小さくできる。特に HTML/CSS/JS などテキスト中心で効果大。(ウィキペディア) 圧縮にかかる CPU 負荷がやや高め。古いクライアント/古いブラウザではサポートされていない可能性。(Kinsta®)
gzip 高い互換性。ほぼすべてのブラウザ/クライアント・サーバ環境で使える。圧縮・解凍も比較的高速。(MDN Web Docs) br に比べると圧縮率がやや低め。特に大きなテキストを多数送る場合は br に劣る。
deflate gzip に似た圧縮方式。gzip と比べてやや軽量かもしれない。(MDN Web Docs) サポート/互換性が若干 gzip に劣ることがあったり、設定や実装差異でトラブルがある場合がある。
identity 圧縮なし — 圧縮/解凍のオーバーヘッドがなく、確実に動く。バイナリやすでに圧縮されたファイル (画像など) に対して安全。 通信量・転送サイズが大きくなるため、非効率。特にテキストが多い Web ページや API レスポンスではパフォーマンス劣化。

なぜ「複数の方式」が並列に指定されることが多いのか

  • クライアント/サーバ間で互換性を保つため: クライアントは複数の方式を列挙しておくことで、「どの方式がサーバで使えるか分からない/状況が変わるかもしれない」場合に備える。
  • フォールバック (fallback) を持たせるため: たとえば br, gzip, deflate, identity のようにしておけば、サーバが br に対応していない場合でも gzip、それもなければ identity で返すという柔軟性が保てる。
  • コンテンツ種類に応じた圧縮の最適化: たとえば JSON / HTML / CSS / JS のようなテキストは高圧縮、画像や動画などすでに圧縮されているバイナリは圧縮しない (identity) — サーバが判断することで変な圧縮による逆効果を防げる。

圧縮方式で失敗するケース

圧縮方式や交渉の仕組みをきちんと理解していないと、次のようなトラブルが起きがちです:

  • クライアントが圧縮方式をサポートしていないのに、サーバが誤って圧縮 (e.g. gzip や br) した → クライアントで解凍できず文字化けやエラー

  • サーバ側で圧縮を強制したけど、クライアントが古い/非対応 — 互換性の問題

  • 圧縮のオーバーヘッド (CPU負荷) によって逆にレスポンス遅延 — 特に高圧縮率の Brotli を多用する場合

ときどき起きる問題

状況:

  • クライアントが Accept-Encoding を付けてない
  • サーバが 勝手に圧縮 (Content-Encoding) して返す
  • クライアントが 解凍処理をしない

結果:

  • 文字化け
  • JSON parse error
  • Unexpected token エラー
  • API クライアント落ちる

調査方法

以下にリクエストを送って、Headerを可視化することはできます。
https://webhook.site/

curl

accept-encoding ヘッダーなし

bruno

accept-encoding ヘッダー自動付与

上記でそれぞれのリクエスト仕組みによって、Accept-encodingが変わることがあり得るので、debugする際には要注意です。

おわりに

今日、この世界にメッセージを送ろうとしている小さな存在がいます。
Web ではクライアントとサーバが互いの意思を伝えるように、
人と人も、理解し合うためには「伝え方」がとても大切です。
Accept-Encoding と Content-Encoding は、
「私はこう受け取れます」「ではこの形で届けます」という
優しいメッセージ交換の約束事。
いつかその小さな存在が誰かとコミュニケーションするときにも、
自分の形をすなおに伝え、相手の形を尊重できる人でありますように。🍼

OPTIMINDテックブログ

Discussion