4124 words
21 min

Content-Dispositionとは?ファイルダウンロードとファイル名について調べてみた

はじめに#

ファイルダウンロードの仕組みを自作しているときに、Content-Disposition というHTTPヘッダーが出てきた。

最初は、

「ファイルをダウンロードさせるために必要なもの」

くらいの認識だった。

ところが、実際に調べてみると、いくつか気になることが出てきた。

Content-Type もファイルの種類を表しているのに、なぜ Content-Disposition も必要なのか?

また、

  • Content-Disposition がなくてもファイルを取得できるのか
  • inlineattachment は何が違うのか
  • filename を指定するとChromeではどう動くのか
  • 日本語のファイル名はどう指定するのか
  • filenamefilename* は何が違うのか
  • 両方指定した場合はどちらが使われるのか

といった疑問も出てきた。

そこで今回は、Node.jsで簡単なHTTPサーバーを作り、Chromeで実際の挙動を確認しながら調べてみた。

この記事の対象読者#

この記事は、以下のような人を対象にしている。

  • Content-Disposition という言葉を初めて知った人
  • HTTPでファイルダウンロードを実装している人
  • Content-TypeContent-Disposition の違いが分からない人
  • filenamefilename* の違いを知りたい人
  • ブラウザがダウンロードファイルの名前をどう決めるのか知りたい人

まず結論#

今回調べてみて、最初に考えていた、

「ファイルをダウンロードさせるには Content-Disposition が必要」

という理解は、少し違っていた。

Content-Disposition は、単純に「ファイルをダウンロードさせるためだけ」のヘッダーではない。

HTTPレスポンスでは、たとえば次のような指定ができる。

Content-Disposition: inline
Content-Disposition: attachment

inline はコンテンツを通常の表示方法で扱うことを示し、attachment はダウンロードする添付ファイルとして扱うことを示す。さらに、filenamefilename* を指定することで、保存時などに使うファイル名についても情報を伝えられる。RFC 6266では、これらの用途が定義されている。

一方、Content-Type は、そのデータがどのようなメディアタイプなのかを示すヘッダー。

そのため、ざっくり整理すると次のようになる。

ヘッダー主な役割
Content-Typeデータの種類を示す
Content-Dispositionレスポンスをどう扱うかを示す
filename保存時などに利用するファイル名を示す
filename*文字コードを指定した形式でファイル名を示す

filename* は拡張パラメータ形式を使って値の文字コードを指定でき、RFC 8187ではUTF-8をサポートすることが定義されている。

ただし、仕様としての定義と、実際のブラウザの挙動が常に同じとは限らない。

そのため、今回はChromeで実際に確認してみる。


Content-Dispositionとは?#

基本的な定義#

HTTPのレスポンスにおける Content-Disposition は、レスポンスのコンテンツをブラウザ内で通常どおり扱うのか、それとも添付ファイルとしてダウンロードするのかといった、コンテンツの扱い方に関する情報を伝えるヘッダー。

代表的な指定は次の2つ。

Content-Disposition: inline
Content-Disposition: attachment

inline はブラウザ内で表示可能なコンテンツを通常どおり扱う方向の指定。

attachment はダウンロードする添付ファイルとして扱う指定。ブラウザによっては、filename が指定されていれば、その名前を保存候補として利用する。


Content-Typeとの違い#

今回、最初に気になったのがここ。

たとえばJPEG画像を返す場合、

Content-Type: image/jpeg

とする。

これは、

「このデータはJPEG画像です」

という情報。

一方、

Content-Disposition: attachment

は、

「このレスポンスは添付ファイルとして扱います」

という情報。

つまり、

Content-Type

「これは何のデータ?」

Content-Disposition

「そのデータをどう扱う?」

という違いとして考えると分かりやすい。

Content-Type はメディアタイプを示し、Content-Disposition はコンテンツの扱い方を示す。


なぜContent-Dispositionが必要になったのか?#

Content-Typeだけでは「どう扱うか」までは決まらない(Content-Typeだけではダメなの?)#

たとえば、

Content-Type: image/jpeg

と返せば、JPEGであることは伝えられる。

ただし、

「そのJPEGをブラウザに表示するのか、保存させるのか」

という情報は別。

そこで、

Content-Disposition: attachment

のような指定が使われる。

今回の検証では、この違いを実際に確認してみる。


Content-Dispositionにはどのような指定がある?#

今回の記事では、主に次の4パターンを確認する。

指定今回確認する内容
なしヘッダーを指定しないとどうなるか
inline通常の表示として扱われるか
attachmentダウンロードとして扱われるか
attachment; filename=...保存時のファイル名が変わるか

さらに、ファイル名について、

filename="sample.txt"

と、

filename*=UTF-8''...

の違いも確認する。


実際にContent-Dispositionを試してみる#

検証環境#

項目内容
OSWindows 11
言語JavaScript
ランタイムNode.js v24.14.1
HTTPサーバーNode.js標準 http
ブラウザGoogle Chrome
検証ファイルTXT / JPEG

Node.jsの response.setHeader() を使うことで、HTTPレスポンスのヘッダーを設定できる。Node.jsでは、ヘッダー値に不正な文字が含まれている場合、TypeError が発生する。また、UTF-8文字をヘッダー値として扱う場合にはRFC 8187形式を使用する例がNode.jsのドキュメントにも掲載されている。


今回確認したこと#

今回の検証では、以下を確認する。

  1. Content-Disposition がない場合、Chromeはどう扱うのか
  2. inlineattachment では何が変わるのか
  3. filename を指定すると保存ファイル名が変わるのか
  4. スペースを含むファイル名はどう扱われるのか
  5. 日本語を filename に直接指定するとどうなるのか
  6. filename* を使うとどうなるのか
  7. filenamefilename* を両方指定するとどちらが使われるのか
  8. JPEGでも同じような挙動になるのか

検証方法#

今回の検証では、ChromeのDevToolsを利用する。

基本的に確認するのは次の3つ。

1. Network#

HTTPレスポンスに実際にどのヘッダーが返ってきたか確認する。

特に、

Response Headers

を確認する。

2. Chromeの画面#

レスポンスをChromeが実際にどう処理したのかを確認する。

たとえば、

  • ブラウザ内に表示された
  • ダウンロードが開始された

など。

3. 保存されたファイル#

ダウンロードが発生した場合は、実際に保存されたファイル名も確認する。


検証1:Content-Dispositionを指定しない#

まずは Content-Disposition をいっさい指定しないケース。

今回使用するTXTでは、

Content-Type: text/plain

だけを返す。

検証コード#

case '/case01-no-disposition-txt':
  sendText(res);
  break;

sendText() の中では Content-Type だけを設定している。

function sendText(res, contentDisposition) {
  res.setHeader('Content-Type', 'text/plain');

  if (contentDisposition !== undefined) {
    res.setHeader('Content-Disposition', contentDisposition);
  }

  res.end(txtData);
}

検証結果#

Response HeadersにContent-Dispositionがないことも確認。
検証1

Chromeの画面にテキストが表示された。

検証結果から分かったこと#

Chromeでは単なるテキストとしてブラウザ表示されたっぽい。


検証2:inline#

次に、

Content-Disposition: inline

を指定してみる。

検証コード#

case '/case02-inline-txt':
  sendText(res, 'inline');
  break;

どうなる?#

仕様上、inline はコンテンツを通常の表示方法で扱う指定。

ただし、今回は「仕様上そうなっている」だけで終わらせず、Chromeで実際に確認してみる。

検証結果#

Response Headersで以下になっていることを確認。
検証2

Content-Disposition: inline

が付与されているが、Chromeの画面にテキストが表示された。

検証結果から分かったこと#

inline を指定してもブラウザ内に表示されるっぽい。


検証3:attachment#

次に、

Content-Disposition: attachment

を指定してみる。

検証コード#

case '/case03-attachment-txt':
  sendText(res, 'attachment');
  break;

attachment はコンテンツをダウンロードする添付ファイルとして扱うための指定。

検証結果#

Response Headersで以下になっていることを確認。

Content-Disposition: attachment

検証3

ダウンロードダイアログが表示されることを確認。
検証3

保存されたファイル名:case03-attachment-txt.txt
検証3


検証4:filenameを指定する#

ここからファイル名指定を確認してみる。

Content-Disposition: attachment; filename="sample.txt"

検証コード#

case '/case04-filename-txt':
  sendText(
    res,
    'attachment; filename="sample.txt"'
  );
  break;

filename は、保存時などに利用するファイル名を示すためのパラメータ。

検証結果#

Response Headersで以下になっていることを確認。

Content-Disposition: attachment; filename="sample.txt"

検証4

ダウンロードダイアログが表示されることを確認。
検証4

保存されたファイル名:sample.txt
検証4

検証結果から分かったこと#

filename に指定した名前で、Chromeでも保存された。


検証5:スペースを含むfilename#

次に、

Content-Disposition: attachment; filename="sample file.txt"

を試してみる。

ファイル名にスペースなどを含める場合、filename の値を引用符で囲む形式が利用できる。

検証コード#

case '/case05-filename-space-txt':
  sendText(
    res,
    'attachment; filename="sample file.txt"'
  );
  break;

検証結果#

Response Headersで以下になっていることを確認。

Content-Disposition: attachment; filename="sample file.txt"

検証5

ダウンロードダイアログが表示されることを確認。
検証5

保存されたファイル名:sample file.txt
検証5

検証結果から分かったこと#

filename にスペースを含む場合は、引用符で囲むことでChromeでも正しくファイル名が保存されるっぽい。


検証6:filenameに日本語を直接指定する#

ここで、次のような日本語ファイル名を指定してみる。

Content-Disposition: attachment; filename="日本語.txt"

検証コード#

case '/case06-filename-japanese-txt':
  sendText(
    res,
    'attachment; filename="日本語.txt"'
  );
  break;

この検証は、ChromeだけでなくNode.js側も重要

Node.jsのHTTP APIでは、ヘッダー値に不正な文字が含まれている場合 TypeError になる。Node.jsのドキュメントでは、UTF-8文字を含む値を扱う場合にRFC 8187形式を使用する例も示されている。

Node.js側のターミナルでエラーが発生した。

TypeError
ERR_INVALID_CHAR

検証6Node.jsエラー

Chrome側はエラーレスポンスが返ってきている。
検証6

検証結果#

Node.js側でエラーが発生したため、Chrome側でレスポンスを受け取れず500エラーになった。
エラーとしては TypeErrorERR_INVALID_CHAR が発生している。


検証7:filename*を使う#

ここで filename* を試してみる。

たとえば、

Content-Disposition: attachment; filename*=UTF-8''%E6%97%A5%E6%9C%AC%E8%AA%9E.txt

とする。

filename* は拡張パラメータ形式を使用し、文字コードを指定した値を表現できる。RFC 8187では、UTF-8''... のような形式で文字コードと値を指定する。

検証コード#

case '/case07-filename-star-txt':
  sendText(
    res,
    "attachment; filename*=UTF-8''%E6%97%A5%E6%9C%AC%E8%AA%9E.txt"
  );
  break;

検証結果#

Response Headersで以下になっていることを確認。

Content-Disposition: attachment; filename*=UTF-8''%E6%97%A5%E6%9C%AC%E8%AA%9E.txt

検証7

ダウンロードダイアログが表示されることを確認。
検証7

保存されたファイル名:日本語.txt
検証7

検証結果から分かったこと#

filename* を使うことで、Chromeでも日本語ファイル名が正しく保存されるようになった。


検証8:filenameとfilename*を両方指定する#

次に、2つを同時に指定してみる。

Content-Disposition: attachment;
 filename="fallback.txt";
 filename*=UTF-8''%E6%97%A5%E6%9C%AC%E8%AA%9E.txt

RFC 6266では、両方が存在する場合、filename* を理解するユーザーエージェントは filename* を選択することが推奨されている。古いユーザーエージェントへのフォールバックとして filenamefilename* の両方を指定する形も示されている。

検証コード#

case '/case08-filename-both-txt':
  sendText(
    res,
    "attachment; filename=\"fallback.txt\"; filename*=UTF-8''%E6%97%A5%E6%9C%AC%E8%AA%9E.txt"
  );
  break;

検証結果#

Response Headersで以下になっていることを確認。

Content-Disposition: attachment; filename="fallback.txt"; filename*=UTF-8''%E6%97%A5%E6%9C%AC%E8%AA%9E.txt

検証8

ダウンロードダイアログが表示されることを確認。
検証8

保存されたファイル名:日本語.txt
検証8

仕様との比較#

仕様上の説明

filename* が優先される

実際のChrome

filename* の指定 (日本語.txt) で保存された

比較

一致した

filename としては "fallback.txt" にしていたが、filename* に日本語を指定したため、日本語.txt の名前で保存された。


検証9:inline + filenameをJPEGで確認する#

ここからJPEGを使ってみる。

レスポンスを次のように設定。

Content-Type: image/jpeg
Content-Disposition: inline; filename="sample.jpg"

検証コード#

case '/case09-inline-filename-jpg':
  sendJpeg(
    res,
    'inline; filename="sample.jpg"'
  );
  break;

ここでは、

filename を指定していても、inline の場合はどうなるのか?

を確認する。

検証結果#

Response Headersで以下になっていることを確認。

Content-Type: image/jpeg
Content-Disposition: inline; filename="sample.jpg"

検証9

保存されたファイル:なし(ダウンロードされず、ブラウザ上で表示された)
検証9

検証結果から分かったこと#

inline はダウンロードダイアログが表示されず、ブラウザ画面で表示されるっぽい。


検証10:JPEG + attachment#

最後に、同じJPEGを attachment にしてみる。

Content-Type: image/jpeg
Content-Disposition: attachment; filename="sample.jpg"

検証コード#

case '/case10-attachment-jpg':
  sendJpeg(
    res,
    'attachment; filename="sample.jpg"'
  );
  break;

検証結果#

Response Headersで以下になっていることを確認。

Content-Type: image/jpeg
Content-Disposition: attachment; filename="sample.jpg"

検証10

ダウンロードダイアログが表示されることを確認。
検証10

保存されたファイル名:sample.jpg
検証10


検証結果をまとめる#

10個の検証を実施したら、最後に表で比較する。

No.ファイルContent-DispositionChromeの結果保存ファイル名
1TXTなし画面表示-
2TXTinline画面表示-
3TXTattachmentダウンロードcase03-attachment-txt.txt
4TXTattachment; filename="sample.txt"ダウンロードsample.txt
5TXTattachment; filename="sample file.txt"ダウンロードsample file.txt
6TXTattachment; filename="日本語.txt"エラー (500)-
7TXTattachment; filename*ダウンロード日本語.txt
8TXTattachment; filename + filename*ダウンロード日本語.txt
9JPEGinline; filename="sample.jpg"画面表示-
10JPEGattachment; filename="sample.jpg"ダウンロードsample.jpg

実際に調べて分かったこと#

調べる前#

最初は、

Content-Disposition は、ファイルをダウンロードさせるために必要なHTTPヘッダー

くらいの認識だった。

調べた後#

調べてみると、Content-Disposition は「ファイルをダウンロードするためだけ」のものではなく、レスポンスを通常どおり扱うのか、添付ファイルとして扱うのかを示すための仕組みだと分かった。

また、filenamefilename* を利用することで、保存時などに利用するファイル名についても伝えられる。

一方、Content-Type はデータのメディアタイプを示すためのもの。

そのため、

Content-Type があるのに、なぜ Content-Disposition も必要なのか?」

という最初の疑問については、

そもそも役割が違うから

という理解になった。


注意点#

今回の検証は、以下の環境で実施している。

  • Windows 11
  • Node.js v24.14.1
  • Google Chrome
  • Node.js標準のHTTPサーバー

そのため、今回のChromeで確認した結果を、そのまま「すべてのブラウザで同じ」と考えることはできない。

特にファイル名については、ブラウザや保存先のファイルシステムによって調整される場合がある。MDNでも、ブラウザがファイルシステム上の制約に合わせてファイル名を変換する場合があると説明されている。

また、Content-Disposition がなくてもブラウザがリソースを取得したり保存したりする場合がある。


今回調べて分からなかったこと#

今回の記事ではChromeに絞って検証している。

そのため、次のような内容は今回の調査範囲外。

  • FirefoxやSafariでの挙動
  • <a download> を利用したダウンロードとの関係
  • 複数ブラウザ間での filename / filename* の比較
  • Windows以外のファイルシステムでのファイル名処理
  • 長すぎるファイル名の詳細な挙動

また時間あるときにでも、調査してみようかな、、、


まとめ#

今回は、ファイルダウンロードを自作しているときに気になった Content-Disposition について調べた。

最初は、

「ファイルをダウンロードさせるために必要なヘッダー」

という認識だった。

しかし、調査と検証を通して、より正確には、

  • Content-Type はデータの種類を示す
  • Content-Disposition はコンテンツの扱い方を示す
  • inline は通常の表示として扱う指定
  • attachment は添付ファイルとして扱う指定
  • filename で保存時などに利用するファイル名を示せる
  • filename* で文字コードを指定できる形式でファイル名を示せる
  • filenamefilename* を両方指定する方法もある

ということが分かった。

最初に疑問だった、

Content-Type があるのに、なぜ Content-Disposition も必要なの?」

については、

Content-TypeContent-Disposition は、そもそも伝えている情報が違うから

という結論になった。

また、

Content-Disposition がないとファイルをダウンロードできないの?」

については、単純に「必須」と考えるのではなく、実際のブラウザがレスポンスをどのように扱うかを確認する必要があるということも分かった。


参考資料#

今回使用したGithubコード群

ryo50
/
content-disposition-test
Waiting for api.github.com...
00K
0K
0K
Waiting...
  • RFC 6266 - Use of the Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)
  • RFC 8187 - Indicating Character Encoding and Language for HTTP Header Field Parameters
  • MDN - Content-Disposition header
  • Node.js v24 HTTP API Documentation
Content-Dispositionとは?ファイルダウンロードとファイル名について調べてみた
https://tech.storias-blog.com/blogs/content_disposition/
作者
Storia
公開日
2026-08-21