【実務・中級編】HTTP/1.1におけるTransfer-Encoding: chunkedの仕組み – HTTPプロトコル・通信規格実践ガイド

終わりの見えないデータに光を:HTTP/1.1 `Transfer-Encoding: chunked` の舞台裏

ネットワークエンジニアとして現場を渡り歩いていると、「なぜかレスポンスが途中で切れる」「動的生成したはずのデータが正しくブラウザに届かない」といった相談をよく受けます。その多くが、HTTP/1.1の根幹を支える `Transfer-Encoding: chunked` の挙動を、概念的にしか理解していないことに起因しています。

今回は、RFC 7230(旧 RFC 2616)で規定されたこの「分割送信」の仕組みを、パケットレベルの挙動からデバッグの実践まで、泥臭く解剖していきましょう。

—

1. なぜ「Content-Length」ではダメなのか?

HTTP/1.0の時代、サーバーはレスポンスを送る前に必ずコンテンツ全体のサイズを計算し、`Content-Length` ヘッダーを付与する必要がありました。しかし、考えてみてください。データベースからストリーミング的にログを吐き出したり、動的に生成される複雑なHTMLを構築したりする場合、「送信を開始するその瞬間に、最終的なバイト数を確定させる」のは至難の業です。

そこで登場したのが `Transfer-Encoding: chunked` です。これは、「全体のサイズはわからないけれど、小分けにした塊(チャンク)を順次送るから、最後に空のチャンクが来たら終わりとみなしてくれ」というプロトコル上の合意です。

—

2. 通信フロー:チャンクの作法

チャンク形式のデータは、以下のような構造でネットワークを流れます。

[チャンクサイズ(16進数)]\r\n
[チャンクデータ]\r\n
[チャンクサイズ(16進数)]\r\n
[チャンクデータ]\r\n
0\r\n
\r\n

1. サイズ指定: 各チャンクの先頭には、そのデータの長さが「16進数」で記述されます。
2. データ本体: 指定されたサイズのデータが続きます。
3. 終端(ターミネーター): サイズ `0` のチャンクが送られた時点で、「これ以降、データはない」とクライアントは判断します。

この仕組みにより、サーバーはメモリ上に全データをバッファリングすることなく、生成したものから順次パイプラインに流し込めるのです。

—

3. 実践:チャンク転送を触ってみる

論よりコード。実際に `curl` を使って、チャンク形式でレスポンスを返してくるサーバーの挙動を覗いてみましょう。

curl で中身を丸裸にする

`curl -v` を実行すると、サーバーがどのようにデータを送り出しているかが手に取るようにわかります。

チャンク転送のヘッダーを確認する
curl -v http://example.com/stream-api

レスポンスヘッダーに `Transfer-Encoding: chunked` が含まれていれば、それはまさにチャンク転送です。

Python (Flask) でチャンクを実装する

バックエンドエンジニアが動的なストリーミングを実装する際の最小構成例です。

from flask import Flask, Response
import time

app = Flask(__name__)

@app.route(‘/stream’)
def stream():
def generate():
# 1秒ごとにチャンクを送り出す
for i in range(5):
yield f”Chunk {i}\n”
time.sleep(1)
yield “Final Chunk”

# Flaskはジェネレーターを渡すと自動的にchunked転送に切り替えます
return Response(generate(), mimetype=’text/plain’)

if __name__ == ‘__main__’:
app.run()

—

4. 現場で役立つデバッグTips

運用中に「チャンク転送がうまくいかない」というトラブルに出くわしたとき、確認すべきポイントは決まっています。

① プロキシ・ロードバランサーの介入

最も多いのが、途中のNginxやロードバランサーがチャンクをバッファリングし、全データが揃うまでクライアントに転送しないケースです。

Nginx設定のチェックポイント:

プロキシ側の設定でバッファリングを無効化する
proxy_buffering off;
チャンクをそのまま透過させる
chunked_transfer_encoding on;

② 「Content-Length」の二重指定

HTTP/1.1の仕様では、`Transfer-Encoding: chunked` を使用する場合、`Content-Length` を付与してはいけません。もし両方存在すると、クライアントはどちらを信頼すべきか混乱し、接続切断(Connection Reset)を引き起こす可能性があります。

③ 終端の「0」が見えているか

ネットワークキャプチャ(Wireshark等)でパケットを追う際は、最後に `0\r\n\r\n` が到達しているかを確認してください。これが欠落している場合、クライアントは「まだ続きがあるはずだ」と待ち続け、タイムアウトエラーとなります。

—

最後に:プロトコルと向き合うということ

`Transfer-Encoding: chunked` は、Webが「固定されたファイル」を届けるメディアから「生きている情報」を伝えるメディアへと進化した歴史の産物です。

仕様書を追うだけでは見えない「なぜここでバッファリングが発生するのか」「なぜこのヘッダーが競合するのか」という問いは、すべてパケットの流れとOSのバッファ管理の中に答えがあります。次に誰かが「レスポンスが途中で止まる」と泣きついてきたら、まずは `Transfer-Encoding` が意図通りに機能しているか、プロキシ層が余計な親切(バッファリング)をしていないかを確認してみてください。

プロトコルを深く理解することは、サーバーのコードを書くこと以上に、強固なインフラを支える鍵になります。現場からは以上です。

コメント

タイトルとURLをコピーしました