巨大なリクエストを投げる前に「話を聞く準備はいい?」と聞く作法:Expect: 100-continue の真実
ネットワークエンジニアとして現場を渡り歩いていると、「なぜか巨大なファイルのアップロードでタイムアウトが多発する」「サーバー負荷が異常に高い」といった相談をよく受ける。その原因を紐解くと、HTTPの通信効率を最適化するための小さな、しかし非常に重要なヘッダーに行き着くことが少なくない。
それが `Expect: 100-continue` だ。
今日は、この「プロトコルの礼儀作法」について、RFCの建前と現場の現実を交えて深掘りしていこう。
—
1. なぜ「100-continue」が必要なのか?
HTTPの通信において、クライアントが数メガバイト、あるいはギガバイト単位の巨大なリクエストボディをサーバーに送ろうとするとき、無条件に全データを送信するのはリスクが高い。
もしサーバー側が「認証エラー」や「リクエストサイズ超過」で拒否する予定だったとしても、クライアントがそれを知らずに数秒間かけてデータを送り続けたらどうなるか? 帯域の浪費だけでなく、サーバーの受信バッファやCPUリソースを無駄に占有することになる。
ここで登場するのが `Expect: 100-continue` ヘッダーだ。これはクライアントからサーバーへの「これからデカい荷物を送るが、受け入れる準備はあるか?」という事前確認(ハンドシェイク)を意味する。
基本フローのシーケンス
1. クライアント: `Expect: 100-continue` を含んだヘッダーのみを送信。
2. サーバー:
- 受け入れ可能なら `100 Continue` を返す。
- 拒否するなら `4xx` や `5xx` を即座に返す。
3. クライアント: `100 Continue` を受け取ったら、初めてボディデータの送信を開始する。
この仕組みにより、サーバーが「No」と言った瞬間に通信を即座に断ち切れるため、無駄なデータ転送を未然に防げるわけだ。
—
2. 実装の現場:コードで確認する挙動
実際に開発の現場でこの挙動を制御・確認する方法を見ていこう。
curl での検証
トラブルシューティングで最も頼りになるのが `curl` だ。`-v` オプションをつけて通信を覗いてみよう。
-v: 詳細表示
-d: ボディデータ
Expectヘッダーを明示的に指定して挙動をテスト
curl -v -X POST http://example.com/upload \
-H “Expect: 100-continue” \
-d “巨大なデータ本体”
サーバーが正常なら、`HTTP/1.1 100 Continue` が先に返り、その後にデータが流れる様子がログから読み取れるはずだ。
Python (requests) でのハンドリング
Pythonでこれを扱う場合、ライブラリがよしなにやってくれることも多いが、意図的に挙動を変えたい場面もある。
import requests
基本的にrequestsはExpect: 100-continueを自動で付与する場合がある
url = “http://example.com/upload”
headers = {“Expect”: “100-continue”}
大きなファイルを開く
with open(“large_file.bin”, “rb”) as f:
response = requests.post(url, data=f, headers=headers)
応答コードを確認してデバッグ
print(f”Status Code: {response.status_code}”)
—
3. インフラ運用における「落とし穴」と最適化
この機能、理論上は完璧だが、運用面ではいくつか注意すべきポイントがある。
サーバー側のタイムアウト設定
サーバー(NginxやApache)は、`Expect` ヘッダーを受け取ってから「100 Continue」を返すまで、あるいはボディが届くのを待機するまでのタイムアウト設定を持っている。
Nginxの場合、`client_body_timeout` がそれにあたる。もしクライアントがリクエストを投げてからサーバーが反応するまでにネットワーク遅延があると、このタイムアウトに引っかかり、サーバーが408(Request Timeout)を返すことがある。
Nginx設定例:
巨大ファイルを受け入れる際のタイムアウト緩和
client_body_timeout 60s;
Expectヘッダーに対する応答の遅延を防ぐ設定などが必要になるケースも
ネットワーク機器の「おせっかい」
ロードバランサーやWAFの中には、`Expect: 100-continue` に対応していない、あるいは不完全な挙動をする古い機器が存在する。これらが間に入ると、リクエストが途中でドロップされたり、クライアントがいつまでも「100 Continue」を待ち続けてハングしたりする。
現場のTips:
特定の環境でアップロードが止まる場合は、まず `Expect` ヘッダーを外して送信してみよう。これで直るなら、経路上のネットワーク機器が「100-continue」のハンドシェイクを正しく解釈できていない可能性が高い。
—
4. 最後に:エンジニアとして持つべき視点
`Expect: 100-continue` は、現代のWeb API設計において「必須」ではない。高速なネットワークが普及した今、小さなリクエストならこのハンドシェイクにかかるRTT(往復遅延)が逆にオーバーヘッドになることもあるからだ。
しかし、大規模なデータ転送や、認証に厳格なAPI設計においては、今なお強力な最適化ツールであることに変わりはない。
トラブルに直面したとき、パケットキャプチャを開いて「100 Continue」が返ってきているかを確認する。その一手間が、数時間かかるデバッグを数分で終わらせる鍵になる。プロトコルは教科書の中にではなく、常に流れているパケットの中にこそ存在するのだ。
皆さんのインフラが、今日も安定してデータを運び続けることを願っている。
コメント