「とりあえず送る」を卒業しよう:HTTP/1.1の `Expect: 100-continue` が語る、ネットワークの礼儀作法
ネットワークエンジニアとして現場を歩いていると、「なぜか大きなPOSTリクエストが途中で詰まる」「認証エラーなのにボディまで全部送られてしまい、帯域が無駄になる」といった相談を受けることがよくある。
現代のWeb API開発において、私たちは往々にして「リクエストは送れば届く」という甘い幻想を抱きがちだ。しかし、数MB、あるいはそれ以上のデータを送信する際、サーバー側の準備が整う前に突撃するのは、決して賢い通信とは言えない。
今回は、HTTP/1.1の隠れた実力者であり、かつトラブルシューティングの現場では「諸刃の剣」にもなり得る `Expect: 100-continue` ヘッダーについて、その深淵を掘り下げていこう。
—
1. なぜ「100-continue」が必要なのか?
HTTP通信において、クライアントが大きなボディ(ファイルアップロードなど)を送信する際、標準的な挙動では「まずヘッダーとボディをセットで投げる」ことが一般的だ。
しかし、もしそのリクエストに認証エラー(401)や、容量オーバー(413)といった拒絶要因があったらどうだろう? クライアントは、サーバーがそれを拒絶するとも知らずに、貴重な帯域と時間を費やして重いデータを送信し続けてしまう。これは、広大なインターネットという共有資源に対する「マナー違反」とも言える。
ここで登場するのが `Expect: 100-continue` だ。このヘッダーを付与することで、クライアントはサーバーに対してこう問いかける。
「これから大きなデータを送るが、受け入れる準備はできているか?」
これに対し、サーバーが「100 Continue」というステータスコードを返して初めて、クライアントは残りのボディデータを送信する。この「事前の握手」こそが、効率的な通信の鍵だ。
—
2. 通信のシーケンス:何が起きているのか
この仕組みの挙動を、パケットキャプチャの視点で整理してみよう。
1. Client -> `POST /upload`, `Expect: 100-continue` を送信
2. Server -> 認証や検証を行う
3. Server -> `100 Continue` をクライアントに返す
4. Client -> 許可を確認し、ボディデータの送信を開始する
もしサーバー側が「このリクエストは受け付けられない」と判断すれば、`100 Continue` を待たずに `401 Unauthorized` や `403 Forbidden` を即座に返す。これにより、無駄なデータ転送を未然に防げるわけだ。
—
3. 実践:クライアント側からのアプローチ
では、実際にこの挙動を制御する方法を見てみよう。
curl での検証
コマンドラインでデバッグする際、`curl` はこの挙動を明示的に確認する最強のツールだ。
-v オプションで詳細なヘッダーのやり取りを確認する
curl -v -X POST http://api.example.com/upload \
-H “Expect: 100-continue” \
-F “file=@large_data.bin”
もしサーバーが正しく対応していれば、レスポンスヘッダーの中に `HTTP/1.1 100 Continue` が現れるはずだ。
Python (requests) での挙動
Pythonの `requests` ライブラリは、デフォルトで `Expect: 100-continue` を送信する挙動をとることが多い。もしこれを明示的にオフにしたい場合(例えば、一部の古いプロキシがこのヘッダーを解釈できずにエラーを返す場合など)は、以下のようにヘッダーを操作する。
import requests
明示的に Expect ヘッダーを空にすることで抑制できる
headers = {
“Expect”: “”
}
サーバー側が 100-continue に対応していない場合、ここを空にすることがトラブル回避の定石
response = requests.post(“http://api.example.com/upload”, data=large_data, headers=headers)
—
4. エンジニアが知っておくべき「落とし穴」
この機能は非常に合理的だが、現場ではいくつかの問題を引き起こすことがある。
- サーバーのタイムアウト: クライアントが `Expect: 100-continue` を送ったのに、サーバーが反応せず無言を貫く場合、クライアントはタイムアウトまで待機することになる。
- 中間プロキシの混乱: ロードバランサーやプロキシサーバーが `100 Continue` の挙動を正しくハンドリングできず、リクエストをドロップしてしまうケースがある。特に古いインフラ機器では注意が必要だ。
- 過剰な期待: サーバー側が「準備完了」と答えても、直後にネットワーク断やプロセスダウンが起きない保証はない。結局のところ、通信エラーの再送制御(Retry)は不可欠だ。
—
まとめ:礼儀を知る通信を目指して
`Expect: 100-continue` は、単なるヘッダーのやり取りではない。それは、「通信の責任を負うクライアント」と「リクエストを吟味するサーバー」の健全な対話を形成するためのプロトコルだ。
Web APIの設計者として、あるいはインフラの運用者として、この挙動を理解しておくことは大きな武器になる。特に大規模なデータ転送を扱うシステムでは、この「事前の握手」があるかないかで、サーバーのリソース負荷や帯域の消費効率が劇的に変わる。
もし、貴方の管理するシステムで原因不明の「アップロード中の詰まり」が発生したら、まずはこの `Expect` ヘッダーが悪さをしていないか、あるいはサーバー側で正しく `100 Continue` を返せているか、パケットを追ってみてほしい。教科書には載っていない、現場の真実が見えてくるはずだ。
コメント