multipart/form-dataのオーバーヘッドを正確に計算する
ファイルアップロードのサイズ上限テストで、「ファイルは9MBなのに413エラーが出る」という経験はありませんか? その原因のひとつが multipart/form-data のオーバーヘッドです。HTMLフォームでファイルをアップロードする際、HTTPリクエストのボディにはファイル本体だけでなく追加のメタデータが含まれます。この記事では、そのオーバーヘッドの正確な計算方法と、テスト時の注意点を解説します。
multipart/form-data の構造
RFC 2046 で定義された multipart/form-data は、各パートを boundary 文字列で区切った構造を持ちます。実際のHTTPリクエストボディはこのようになります。
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryABC123
Content-Length: 10000xyz
------WebKitFormBoundaryABC123
Content-Disposition: form-data; name="file"; filename="test.png"
Content-Type: image/png
[ファイルのバイナリデータ]
------WebKitFormBoundaryABC123--
オーバーヘッドの内訳
典型的なファイルアップロードリクエストのオーバーヘッドは以下の要素で構成されます。
| 要素 | 例 | バイト数(目安) |
|---|---|---|
| 開始 boundary | ------WebKitFormBoundaryABC123\r\n | 約 40〜80 B |
| Content-Disposition ヘッダー | Content-Disposition: form-data; name="file"; filename="test.png"\r\n | 約 60〜120 B |
| Content-Type ヘッダー | Content-Type: image/png\r\n | 約 25〜50 B |
| 空行(ヘッダー末尾) | \r\n | 2 B |
| パート末尾の改行 | \r\n | 2 B |
| 終了 boundary | ------WebKitFormBoundaryABC123--\r\n | 約 42〜82 B |
| 合計オーバーヘッド | 約 200〜350 B |
ファイル1つだけのシンプルなフォームなら、オーバーヘッドは 200〜400バイト程度 です。追加のフォームフィールド(テキスト入力など)がある場合はその分増えますが、一般的には数百バイト〜数KB程度に収まります。
なぜ413エラーが発生するのか
Nginx の client_max_body_size は リクエストボディ全体 のサイズを制限します。つまり、設定値にはオーバーヘッドを含む必要があります。
# 10MiB のファイルをアップロードさせたい場合
# オーバーヘッド(約1KB)を考慮して少し大きめに設定
client_max_body_size 11m; # MiB単位: 11 MiB = 11,534,336 バイト
PHP の場合、upload_max_filesize(ファイル単体)と post_max_size(POSTボディ全体)の2つを設定する必要があります。
; php.ini
upload_max_filesize = 10M ; ファイル単体の上限: 10 MiB
post_max_size = 11M ; POSTボディ全体の上限: 11 MiB(オーバーヘッド分を加算)
正確なオーバーヘッドの測定方法
実際のオーバーヘッドを正確に知りたい場合は、curl でリクエストを送信してリクエストサイズを計測できます。
# ファイルのバイト数を確認
wc -c test-10mb.png
# → 10485760 test-10mb.png
# curl でアップロードしてリクエストサイズを確認
curl -X POST https://example.com/upload \
-F "file=@test-10mb.png" \
-w "リクエストボディサイズ: %{size_upload} バイト\n" \
-o /dev/null -s
# → リクエストボディサイズ: 10486062 バイト(差: 302バイト)
Base64エンコードとの違い
multipart/form-data の場合、ファイルのバイナリデータはそのまま送信されます(Base64エンコードなし)。Base64エンコードが必要になるのは、application/x-www-form-urlencoded でバイナリを送る場合や、JSONボディでファイルを送る場合(data:image/png;base64,...`)です。
| 送信方式 | オーバーヘッド | 用途 |
|---|---|---|
| multipart/form-data(HTMLフォーム) | 数百B〜数KB | 通常のファイルアップロード |
| JSON + Base64 | 約33%増加 | API経由でのファイル送信 |
| application/octet-stream(PUT) | ほぼゼロ | S3 presigned URL など |
テスト時のポイント
- サーバーの上限チェックが「ファイル単体」か「リクエストボディ全体」かを把握する
- NginxはPHPの前段でリクエストを制限するため、Nginxの
client_max_body_sizeも確認する - 複数ファイルの同時アップロードはオーバーヘッドが増える
- 追加フォームフィールド(名前、コメントなど)の分もオーバーヘッドに含まれる
DevLab のしきい値テスト用ファイルを使えば、境界付近のファイルサイズで実際のアップロード動作を確認できます。Nginx/Apache/PHPの設定と合わせて検証してください。
まとめ
multipart/form-dataのオーバーヘッドは 200〜400バイト程度(ファイル1つの場合)- Nginx の
client_max_body_sizeはリクエストボディ全体を制限するため、ファイル上限より少し大きく設定する - PHPは
upload_max_filesize(ファイル単体)とpost_max_size(POSTボディ全体)の2つを設定する - JSON + Base64 で送信する場合はファイルサイズが約33%増加する