同じPDFでも、リンクを開くと画面に表示される場合と、保存を促される場合があります。HTTP応答のContent-Dispositionは、その内容をどう扱ってほしいか、保存時にどんな名前を候補にするかを伝えるヘッダーです。
たとえば、日本語の「資料.pdf」という名前を提案しても、実際の保存先や使用する名前は受信側で決まります。応答の扱い方から名前の読み取り、保存までを順に追うと、ヘッダーが担う役割が見えてきます。本稿ではHTTP応答ヘッダーを扱います。フォーム送信のmultipart内にある同名フィールドは対象に含めません。
簡単にまとめると?
- attachmentは保存を促す扱い、inlineは通常の処理を示します。Content-Typeが表す内容の種類とは別の情報です。
- filenameは名前の候補、filename*は文字コードを伴う拡張形式です。日本語名と、非対応の受信側向けの英数字名を組み合わせられます。
- 名前は提案として扱います。受信側はパス・拡張子・制御文字などを確認し、指定された任意の場所への保存を許しません。
何のデータかと、どう扱うかは別
Content-Typeは、たとえばapplication/pdfのように内容のメディアタイプを表します。対してContent-Dispositionは、その応答内容の扱いに関する追加情報です。PDFという内容の種類を伝えたうえで、保存を促してほしいと指定できるわけです。
RFC 6266では、attachmentは通常のメディアタイプに基づく処理よりもローカル保存を利用者へ促すことを示し、inlineは通常の処理を示します。inlineにfilenameを添える場合は、その場では表示し、あとで保存するときの名前の候補として使うことも考えられます。
実際に出る画面や保存の手順は、ブラウザーなど受信側の機能・設定にも左右されます。attachmentを付ければ、どの環境でも同じダイアログになるわけではありません。
filename*で日本語の名前を伝える
filenameは保存名を組み立てるための情報です。たとえばreport.pdfという英数字名なら、そのまま候補を表せます。一方、日本語などを含む名前には、文字コードを明示できるfilename*の形式を使えます。末尾のアスタリスクは飾りではなく、値を読む形式の違いを示します。
filename*には、文字コード、任意の言語情報、符号化した値を並べます。UTF-8を使い、言語を空にした形なら、先頭はUTF-8''となります。その後ろに、必要なバイトをパーセント表記した名前が続きます。これはHTTP本文全体を変換する処理ではなく、ヘッダー内の名前を表すための仕組みです。
両方のパラメーターを置く構成では、対応する受信側はfilename*を優先し、filenameを無視することが推奨されています。filenameの側には古い受信側向けの候補を残せます。ただし同じ名前のパラメーターを何度も並べることは別問題で、RFC 6266では同名パラメーターの重複は不正な値です。
「資料.pdf」をバイトへほどく
独自の例として、保存名の候補を「資料.pdf」とします。「資」のUTF-8はE8 B3 87、「料」はE6 96 99です。日本語部分の各バイトをパーセント表記すると、%E8%B3%87%E6%96%99.pdfになります。アスタリスク付きの値は、UTF-8''%E8%B3%87%E6%96%99.pdfです。
非対応の受信側に渡す候補をreport.pdfとすると、二つの名前が同じ応答に共存します。ここで別々のPDFを送っているわけではありません。一つの応答本文に対して、受信側の対応に応じた名前の情報を添えています。

このパーセント表記は、filename*の拡張形式として読み取るためのものです。通常のfilenameへ入れると、受信側によっては表記がそのまま名前に残るなど、意図と異なる結果になり得ます。空白や特殊文字を扱うときも、URL・フォーム・ヘッダーパラメーターそれぞれの符号化の規則に合わせます。
読み取った名前を、そのまま保存に使わない理由
「資料.pdf」を読み取れたら、受信側は保存に使える名前かを判断します。サーバーの指定は名前の提案なので、フォルダー情報が含まれていても、ローカルの保存先を自由に選ばせてはいけません。RFC 6266は、スラッシュやバックスラッシュで区切られたパスの最後の要素だけを取り出す方法を例示しています。
取り出した名前にも手当てが必要です。制御文字や前後の空白は取り除くか置き換え、OSで特別な意味を持つ名前は避けます。拡張子をもとにファイルの種類を判断する環境では、サーバーの指定を信用すると、あとで開く際に危険な処理へつながるおそれがあります。受け取った内容の種類に合う安全な拡張子を選ぶ必要があり、名前が.pdfであることだけでは内容が正しいPDFかどうかは分かりません。
こうして名前を整えたあとも、利用者が選ぶ保存先や、同名の既存ファイルがある場合の扱いは受信側で決めます。ヘッダーに名前があることを理由に、任意の場所へ書き込んだり、既存ファイルを無条件に上書きしたりはしません。
表示や保存が想定と違ったら、どこを見るか
保存を促したいのに画面で開かれるなら、まず送信した応答のContent-Dispositionがattachmentになっているかを調べます。そのうえで、受信側の設定や実装を確認します。Content-Typeと実際の内容が一致しているかも、応答を調べる段階で押さえます。
日本語の名前が崩れるなら、filename*の指定とUTF-8のバイト列を確認します。「資料」の符号化と復号が正しく往復できても、受信側がfilename*に対応しているとは限りません。report.pdfが選ばれた場合は、filenameの候補が使われた可能性があります。同名パラメーターを重複させていないかも確認します。
名前が読めるのに保存時に変わるなら、受信側が名前を安全化したり、同名ファイルとの衝突を処理したりしている可能性があります。対象の環境で、表示・保存・再保存まで確かめると、どの段階で変わったかを追えます。本稿で確認したのは例のUTF-8バイト列の計算であり、ブラウザー間の互換性試験は行っていません。
Content-Dispositionは、応答をどう扱い、どんな名前で保存してほしいかを伝えます。受信側がその提案を読み取り、利用環境に合う保存方法を決めるところまでが、一連のダウンロード処理です。
出典
- RFC 6266 — Use of the Content-Disposition Header Field in HTTP(2011年6月)。Sections 3、4.1〜4.3、5を参照。
- RFC 8187 — Indicating Character Encoding and Language for HTTP Header Field Parameters(2017年9月)。Sections 3.2、3.2.1を参照。RFC 6266が引用するRFC 5987を置き換えた文字コード表現の仕様。
