メディアタイプの構造
MIME タイプ——正式にはメディアタイプ——は、続くバイト列をどう扱えばよいかをクライアントに伝えるために、サーバーがレスポンスへ付けるラベルです。スラッシュで区切られた 2 つの部分から成ります。text、image、audio、video、font、model、application といったトップレベルタイプと、具体的な形式を示すサブタイプです。image/png や application/json は完全なメディアタイプですが、image 単体はそうではありません。
サブタイプにも独自の慣習があります。先頭の x- はかつて実験的なタイプを示すもので、application/x-tar がその例です。この慣行は非推奨になりましたが、生き残った名前は広く普及しすぎて変更できません。末尾の +接尾辞 は、その形式がより一般的な構文の上に成り立っていることを示します。image/svg+xml は XML、application/ld+json は JSON であり、パーサーは具体的な形式を認識できないときに基底の構文へフォールバックできます。
セミコロンの後にパラメータを取れるタイプもあります。最も重要なのは charset で、text/html; charset=utf-8 のように使います。テキストレスポンスでこれを省くとブラウザに文字コードの推測を委ねることになり、文字化けの典型的な原因になります。UTF-8 を明示する数文字は常に払う価値があります。
拡張子よりヘッダーが重要な理由
ブラウザは URL 中のファイル名ではなく Content-Type ヘッダーを見てレスポンスの扱いを決めます。PNG を text/plain として返せばバイナリの羅列が表示され、JSON を text/html として返せば fetch クライアントが拒否することもあります。ヘッダーが正しいからこそ、ダウンロードは適切なアプリで開き、インラインの素材はダウンロードされずに表示されるのです。
不一致にはセキュリティ上の影響もあります。古いブラウザは MIME スニッフィングを行い、レスポンスの先頭バイトを調べて誤っていると判断したヘッダーを上書きしていました。HTML に見えるファイルをアップロードできる攻撃者は、サーバーが画像とラベル付けしていてもサイトのオリジンでそれを実行させられた可能性があります。X-Content-Type-Options: nosniff ヘッダーはこの挙動を無効化し、ユーザー投稿コンテンツを配信するエンドポイントでは今や標準的な対策です。
ダウンロードでは、メディアタイプを Content-Disposition: attachment; filename="report.pdf" と組み合わせます。メディアタイプはバイト列が何であるかを、disposition はブラウザがそれをどうすべきかを表しており、両者は別の問いに答えています。
その対応表は実際どこにあるのか
拡張子からタイプへの唯一絶対の表は存在しません。これは意外に思われがちです。IANA が登録するのはメディアタイプそのものであり、ファイル拡張子からタイプへの対応は各サーバーが維持するローカルな慣習です。Apache は mime.types を読み、nginx も独自の mime.types を持ち、アプリケーションフレームワークもそれぞれの参照表を同梱しています。同じ .wasm ファイルがあるサーバーでは正しく配信され、更新されていない別のサーバーでは application/octet-stream になるのはこのためです。
application/octet-stream は「任意のバイナリデータ」を意味する汎用のフォールバックです。形式が本当に不明なときには誠実な答えですが、インライン表示は機能しなくなるため、サイトが日常的に配信する形式には明示的な対応付けを追加する価値があります。AVIF、JXL、WebAssembly といった新しい形式は、古いサーバー設定で最も欠落しやすい項目です。