HTTP において{{Gengoheiki("コンテンツネゴシエーション", "content negotiation")}} は、同じ URL に対してさまざまなバージョンのリソースを提供するために使用する仕組みであり、ユーザーエージェントはどのリソースがユーザーにもっとも適しているか (例えばドキュメントの言語、画像の形式、コンテンツのエンコード方式) を指定できます。
コンテンツネゴシエーションの原理
具体的なドキュメントは{{Gengoheiki("リソース", "resource")}} と呼ばれます。クライアントがリソースを取得したいときは、URL を使用して要求します。サーバーはこの URL を、提供するものをバリエーションからひとつ選択するために使用します (それぞれのバリエーションは{{Gengoheiki("表現", "representation")}} と呼ばれます)。そして、特定の表現をクライアントに返します。それぞれの表現を含むすべてのリソースが、固有の URL を持ちます。リソースが呼び出されたときに特定の表現を選択する方法はコンテンツネゴシエーションによって決められ、クライアントサーバーの間で交渉する方法がいくつかあります。
もっとも適した表現の決定は、以下の 2 つの仕組みのいずれかによって行われます:
- クライアントによる特定の HTTP ヘッダー (サーバー駆動型ネゴシエーション または プロアクティブネゴシエーション)。これは、特定の種類のリソースで交渉を行う標準的な方法です。
- サーバーによる {{HTTPStatus("300")}} (Multiple Choices) または {{HTTPStatus("406")}} (Not Acceptable) HTTP レスポンスコード (エージェント駆動型ネゴシエーション または リアクティブネゴシエーション)。これはフォールバック機構として使用します。
数年来、{{Gengoheiki("透過的コンテンツネゴシエーション", "transparent content negotiation")}} や Alternates
ヘッダーといった他のコンテンツネゴシエーションが提案されてきました。これらは支持を得られず、破棄されました。
サーバー駆動型コンテンツネゴシエーション
サーバー駆動型コンテンツネゴシエーション またはプロアクティブコンテンツネゴシエーションでは、ブラウザー (または他のユーザーエージェント) が URI と共にいくつかの HTTP ヘッダーを送信します。これらのヘッダーは、ユーザーにとって好ましいものを表します。サーバーではそれらを手がかりとして使用して内部アルゴリズムが、クライアントに提供する最善のコンテンツを選択します。そのアルゴリズムはサーバーによって異なり、標準化されていません。例として、Apache 2.2 のネゴシエーションアルゴリズム をご覧ください。
HTTP/1.1 標準では、サーバー駆動型ネゴシエーションを開始する標準ヘッダーの一覧 ({{HTTPHeader("Accept")}}, {{HTTPHeader("Accept-Charset")}}, {{HTTPHeader("Accept-Encoding")}}, {{HTTPHeader("Accept-Language")}}) を定義しています。厳密に言えば {{HTTPHeader("User-Agent")}} はこの一覧に含まれていませんが、要求したリソースの特定の表現を送信するために使用されることがあります。ただし、これはよい慣習ではないと考えられています。サーバーはどのヘッダーを実際にコンテンツネゴシエーションで使用したかを示すために {{HTTPHeader("Vary")}} ヘッダー (あるいは、より的確な関係があるレスポンスヘッダー) を使用します。これにより、キャッシュ が適切に機能します。
さらに、ネゴシエーションに使用できるヘッダーを追加する実験的な提案があり、client hints と呼ばれています。client hints は、ユーザーエージェントを実行しているデバイスがどのようなものか (例えば、デスクトップコンピューターかモバイルデバイスか) を伝えます。
サーバー駆動型コンテンツネゴシエーションはリソースの特定の表現を決定するためのもっとも一般的な方法ですが、いくつか欠点があります:
- サーバーは、ブラウザーのことをすべて知っているわけではありません。Client Hints 拡張を加えても、ブラウザーの機能を完全には把握できません。クライアントが選択するリアクティブコンテンツネゴシエーションとは異なり、サーバーの選択はすべて若干独断的です。
- クライアントが提供する情報はかなり冗長であり (HTTP/2 のヘッダー圧縮は、この問題を緩和します)、またプライバシーのリスク (HTTP フィンガープリンティング) もあります。
- 指定されたリソースの複数の表現を送信すると、共有キャッシュの効率が下がります。また、サーバーの実装はより複雑になります。
Accept
ヘッダー
{{HTTPHeader("Accept")}} ヘッダーは、エージェントが処理することを望むメディアリソースの MIME タイプを羅列します。これはカンマ区切りで MIME タイプで並べており、それぞれの MIME タイプは、別の MIME タイプとの相対的な優先度を示すパラメータであるクオリティファクターと結びつけられています。
{{HTTPHeader("Accept")}} ヘッダーはブラウザーまたは他のユーザーエージェントによって定義され、それは HTML ページ・画像・動画・スクリプトなど取得するものによって変わる場合があります。アドレスバーで指定したドキュメントを取得するときと {{HTMLElement("img")}}、{{HTMLElement("video")}}、{{HTMLElement("audio")}} 要素でリンクしたものを取得するときで、このヘッダーは異なります。ブラウザーはこのヘッダーで、最適と思われる値を自由に使用できます。一般的なブラウザーの既定値 の包括的な一覧があります。
Accept-CH
ヘッダー {{experimental_inline}}
これは Client Hints と呼ばれる実験的な技術の一部であり、現在は Chrome 46 以降だけが実装しています。
実験的な {{HTTPHeader("Accept-CH")}} は、サーバーが適切なリソースを選択するために使用できる設定データを羅列します。有効な値は以下のとおりです:
値 | 意味 |
---|---|
DPR |
クライアントのデバイスピクセル比を示します。 |
Viewport-Width |
レイアウトビューポートの幅を CSS ピクセルで示します。 |
Width |
リソースの幅を物理ピクセルで示します (言い換えると、画像の本来の幅です)。 |
Accept-Charset
ヘッダー
{{HTTPHeader("Accept-Charset")}} ヘッダーは、ユーザーエージェントが理解する文字エンコーディングが何かを、サーバーに示します。伝統的に、例えば西ヨーロッパロケールでは ISO-8859-1,utf-8;q=0.7,*;q=0.7
のように、ブラウザーのために各ロケールへさまざまな値を設定していました。
UTF-8 が良好にサポートされるようになり、好ましい文字エンコード方法になっています。また、設定に基づくエントロピーを減らしてプライバシーを保護するために、ほとんどのブラウザーが Accept-Charset
ヘッダーを省略しています。Internet Explorer 8、Safari 5、Opera 11、Firefox 10 はこのヘッダーの使用を取りやめました。
Accept-Encoding
ヘッダー
{{HTTPHeader("Accept-Encoding")}} ヘッダーは、受け入れ可能な content-encoding (サポートする圧縮方式) を定義します。この値は、エンコーディングの優先度を示す Q 値のリスト (例: br, gzip;q=0.8
) です。既定値 identity
は (ほかに宣言されていなければ) 優先度が最低です。
HTTP メッセージの圧縮はウェブサイトのパフォーマンスを向上させるためにもっとも有力な手段のひとつであり、転送するデータのサイズを削減して利用可能な帯域を有効活用します。ブラウザーは常にこのヘッダーを送信します。またサーバーはこのヘッダーを受け入れるように設定して、圧縮を行うべきです。
Accept-Language
ヘッダー
{{HTTPHeader("Accept-Language")}} ヘッダーは、ユーザーの言語設定を示すために使用します。これは、quality factor を伴う値のリストです (例: "de, en;q=0.7
")。既定値はたいてい、ユーザーエージェントのグラフィカルインターフェイスの言語に従いますが、ほとんどのブラウザーでは異なる言語設定を適用できます。
変更された値はユーザーのフィンガープリントとして使用できることから 設定に基づくエントロピー が高まるため、値を変更することは推奨されていません。またウェブサイトは、この値がユーザーの本当の希望を反映していると信じてはいけません。ユーザー体験を低下させる可能性があるため、サイトデザイナーはこのヘッダーで言語を検出することに熱心になってはいけません:
- サイトデザイナーは、サーバーが選択した言語を変える手段を常に提供するべきです。例えば、サイトで言語メニューを提供します。例えばインターネットカフェでは、多くのユーザーエージェントは
Accept-Language
ヘッダーに、ユーザーインターフェイスの言語に適合する既定値を設定しており、またユーザーはたいていその設定を変更しません。これは変更方法を知らない、あるいは変更できないためです。 - サーバーが選択した言語をユーザーが上書きしたら、サイトは言語検出を使用せず、明示的に指定された言語に従うべきです。言い換えると、サイトの入り口のページだけがこのヘッダーを使用して適切な言語を選択するべきです。
User-Agent
ヘッダー
このヘッダーをコンテンツの選択に使用することは理にかなっていますが、ユーザーエージェントがどの機能をサポートしているかを判断するためにこのヘッダーを頼ることは 悪い習慣であると考えられています。
{{HTTPHeader("User-Agent")}} ヘッダーは、リクエストを送信するブラウザーを明らかにします。この文字列には、空白区切りでプロダクトトークンやコメントのリストが含まれるでしょう。
プロダクトトークンは Firefox/4.0.1
のように、名称、スラッシュ '/
'、バージョン番号で構成されます。これはユーザーエージェントの必要性に応じて、複数存在することがあります。コメント は、括弧で囲まれた自由な文字列です。当然ながら、その文字列内で括弧を使用することはできません。コメントの内部形式は標準化されておらず、従って各ブラウザがさまざまなトークンをセミコロン ';
' 区切りで入れています。
Vary
レスポンスヘッダー
クライアントが送信する前出の Accept-*
ヘッダーとは対照的に、{{HTTPHeader("Vary")}} HTTP ヘッダーはウェブサーバーがレスポンスで送信します。これは、サーバーがサーバー駆動型コンテンツネゴシエーションで使用したヘッダーのリストを示します。このヘッダーはネゴシエーションを再現できるように、判断基準のキャッシュを知らせるために必要であり、キャッシュが機能を果たすようにするとともに、ユーザーに誤ったコンテンツを提供することを防ぎます。
特別な値 '*
' は、サーバー駆動型コンテンツネゴシエーションで適切なコンテンツを選ぶために、ヘッダーで与えられていない情報も使用することを表します。
Vary
ヘッダーは HTTP バージョン 1.1 で追加され、キャッシュが適切に働くようにするために必要です。エージェント駆動型コンテンツネゴシエーションが機能するには、転送されたコンテンツを選択するためにサーバーが使用した基準を知るためのキャッシュが必要です。この方法で、キャッシュはコンテンツ選択のアルゴリズムを再生することを可能にして、サーバーへさらにリクエストを行うことなく適切なコンテンツを直接提供できるでしょう。当然ながらワイルドカード '*
' は、背後にある要素をキャッシュで知ることができないため、キャッシュの生成を妨げます。
エージェント駆動型ネゴシエーション
サーバー駆動型ネゴシエーションには少々弱点があり、うまくスケーリングできません。ネゴシエーションでは、ひとつの機能に対してひとつのヘッダーを使用します。例えばスクリーンサイズ、解像度、または他の寸法を使用したい場合は、新たな HTTP ヘッダーを作成しなければなりません。またヘッダーは、すべてのリクエストで送信しなければなりません。ヘッダーが少ない場合は問題になりにくいのですが、それらを掛け合わせた結果メッセージサイズがパフォーマンス低下の原因になるかもしれません。多くの詳細なヘッダーを送信するとエントロピーも多く送信されますので、HTTP フィンガープリンティングの可能性やそれに伴うプライバシーの懸念が増大します。
HTTP は初期のうちから、もうひとつのネゴシエーション方法であるエージェント駆動型ネゴシエーションまたはリアクティブネゴシエーションを使用できます。このネゴシエーションではあいまいなリクエストが発生したときに、サーバーは使用可能なリソースへのリンクを含むページを返します。ユーザーにはそれらのリソースが差し出されて、そこから使用するものを選択します。
残念ながら HTTP 標準では使用可能なリソースを選択できるページの様式を定めておらず、このプロセスを容易に自動化できない状況です。サーバー駆動型ネゴシエーションのフォールバックのほかにも、この方法はほとんどの場合スクリプト、特に JavaScript のリダイレクトともに使用します。ネゴシエーション基準を確認した後、スクリプトがリダイレクトを実行します。第二の問題点は、実際のリソースを取り出すために複数のリクエストが必要であるため、ユーザーがリソースを利用可能になるのが遅れることです。