【実務・中級編】HTTP/1.1におけるExpect: 100-continueの挙動 – HTTPプロトコル・通信規格実践ガイド

巨大なリクエストを送り出すその前に——「Expect: 100-continue」という名の慎重なハンドシェイク

ネットワークの現場で「なぜか特定のAPIだけアップロードが遅い」「認証エラーなのに巨大なファイルを送りつけて帯域を無駄にしている」といった事象に遭遇したことはないだろうか。

HTTP/1.1で策定された `Expect: 100-continue` は、一言で言えば「大きな荷物を運び出す前に、相手が受け取れる状態かを確認する」ための紳士的なプロトコルだ。これを正しく理解し実装することは、単なる仕様の把握を超え、堅牢なAPI設計と帯域効率化への第一歩となる。今日は、この隠れた名脇役の挙動を解剖していこう。

—

1. なぜ「100-continue」が必要なのか

Web開発の現場では、数MBから時にはGB単位のファイルをPOSTする機会がある。もし、サーバー側で「認証トークンが期限切れ」だったり、「リクエストボディのサイズ制限に抵触」していたりした場合、どうなるだろうか。

標準的なHTTP通信では、クライアントはリクエストヘッダーとボディをセットで送信する。しかし、サーバーが後からエラーを返したときには、既に巨大なデータが回線を駆け抜けた後だ。これでは帯域の無駄遣いであり、低速回線やモバイル環境では致命的なUXの低下を招く。

ここで登場するのが `Expect: 100-continue` だ。
クライアントはまず「このヘッダーで送っていい?」とサーバーに問いかけ、サーバーが「OK、続きを送れ(100 Continue)」と返してから、満を持してボディを送信する。この「二段階認証」のようなプロセスが、無駄なデータ転送を防ぐ鍵となる。

—

2. 通信のシーケンス:パケットの往復を可視化する

このハンドシェイクの裏側では、以下のパケットフローが繰り広げられている。

1. Client: `POST /upload HTTP/1.1` + `Expect: 100-continue` ヘッダーのみを送信。
2. Server: 内容を確認し、受け入れ可能なら `HTTP/1.1 100 Continue` を返信。
3. Client: それを受け取ってから、本番の `Request Body` を流し込む。
4. Server: 処理完了後、最終的なレスポンス(`200 OK` など)を返す。

もしサーバーがこの仕様を知らない、あるいは拒否する場合は `417 Expectation Failed` が返されるため、クライアント側はその時点で送信を中止できる。

—

3. 実践:コードで挙動を制御する

実際にこの挙動を検証・実装するためのTipsを紹介しよう。

curl での検証

CLIでデバッグする際、`curl` はデフォルトでこの挙動を考慮している。意図的に試すには以下のように記述する。

-v で詳細なハンドシェイクの様子を表示
Expect: 100-continue を明示的に付与してリクエスト
curl -v -X POST http://api.example.com/upload \
-H “Expect: 100-continue” \
-T large_file.bin

Python (requests) でのハンドシェイク

`requests` ライブラリは、デフォルトでは `Expect: 100-continue` を送らないことが多い。明示的に制御したい場合は、ヘッダーに含める必要がある。

import requests

url = “http://api.example.com/upload”
headers = {
“Expect”: “100-continue”
}

大きなデータを開いて送信
with open(“data.bin”, “rb”) as f:
response = requests.post(url, data=f, headers=headers)

レスポンスコードを確認
print(f”Status Code: {response.status_code}”)

—

4. 運用上の注意点:タイムアウトの罠

シニアエンジニアとして、最後に「現場の落とし穴」を伝えておきたい。

このハンドシェイクには「クライアント待機問題」がある。サーバーが `100 Continue` を返すのに時間がかかっている場合、クライアントはいつまで待つべきか? RFC 2616(および現在のRFC 9112)では、クライアントは永久に待つべきではないとされている。

実務上、多くのクライアントライブラリは、約1秒から3秒程度のタイムアウトを設けている。

  • サーバーサイドの負荷: もしバックエンドの認証ロジックが重く、`100 Continue` を返す前に処理が詰まると、クライアントはタイムアウトしてリクエストを打ち切る。
  • プロキシ/ロードバランサー: NginxやAWS ALBを挟む場合、これらの設定で `Expect` ヘッダーの処理が意図せず阻害されることがある。Nginxなら `proxy_set_header Expect $http_expect;` を適切に設定しないと、バックエンドまでリクエストが正しく透過されないケースがある。

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

location /upload {
# クライアントからのExpectヘッダーをバックエンドに渡す設定
proxy_set_header Expect $http_expect;

# タイムアウト調整
proxy_read_timeout 60s;
}

—

まとめ:ネットワークの「礼儀」を理解する

`Expect: 100-continue` は、HTTPという巨大なプロトコルのなかで、クライアントとサーバーが「合意」を形成するための、極めて知的な仕組みだ。

大規模なデータを扱うAPIを設計する際は、単に「送る」ことだけでなく、「いつ送る許可を出すか」までを考慮に入れてほしい。それが、無用なサーバー負荷を減らし、安定した通信を実現するプロのエンジニアの流儀である。

今日からあなたのアプリケーションが、ネットワークに対して少しだけ「礼儀正しく」振る舞えるようになることを願っている。もしトラブルが起きたら、まずはパケットキャプチャで `100 Continue` が正しく返ってきているか確認することから始めよう。現場からは以上だ。

コメント

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