やったこと
roadmapを進めた
Internet, what is HTTP?
MDNの資料を読んだ
HTTP
Hypertext Transfer Protocol (HTTP) は HTML などのハイパーメディア文書を転送するためのアプリケーション層プロトコル
HTTP はいわゆるステートレスプロトコルであり、つまりサーバーは二つのリクエスト間で何もデータを保持しません。
1990 年代初頭に設計された HTTP は、時間をかけて進化した拡張可能なプロトコルです。 HTTP は、 TCP または TLS (暗号化された TCP 接続) を使用して送信されるアプリケーション層のプロトコルですが、理論上は信頼性のある任意のトランスポート層プロトコルを使用できます。
HTTPベースシステムの構成要素
個々のリクエストはサーバーに送信され、処理した後にレスポンスと呼ばれる回答を提供します。
クライアントとサーバーとの間には、例えばゲートウェイやキャッシュなどの様々な操作を行う、まとめてプロキシサーバーと呼ばれるいくつもの実体が存在しています。
- クライアント: ユーザーエージェント
- ユーザーエージェントは、ユーザのために働くツールです。この役割は主に、ウェブブラウザーが担います。
- ブラウザーは常に、リクエストを生成する実体です。サーバーにはなりません
- ウェブページは、ハイパーテキスト文書です。これは表示されているテキストの一部が新たなウェブページの取り込みを (通常、マウスのクリックによって) 発生させるリンクであり、ユーザーがユーザーエージェントを導いてウェブ内を移動できるということです。
- ウェブサーバー
- 通信路の反対側は、クライアントのリクエストに応じて文書を提供するサーバーがいます。
- サーバーは 1 台のマシンである必要性はありませんが、複数のサーバーのソフトウェアインスタンスを同じマシンで運用することができます。 HTTP/1.1 と Host ヘッダーによって、同じ IP アドレスを共有できます。
- プロキシ
- ウェブブラウザーとウェブサーバーの間では、多数のコンピューターや端末が HTTP メッセージを中継します。
- プロキシの機能
- キャッシュ (キャッシュは共用、あるいはブラウザーキャッシュのように個人用にできます)
- フィルタリング (アンチウィルススキャンやペアレンタルコントロールなど)
- 負荷分散 (複数のサーバーが別々のリクエストに対応できるようにする)
- 認証 (さまざまなリソースへのアクセスを制御する)
- ログ記録 (履歴情報の保管を可能にする)
HTTP の基本方針
- HTTP はシンプル
- HTTP/2 で HTTP メッセージをフレームにカプセル化することにより複雑さが増しましたが、 HTTP は全体的にシンプルで人間が読めるように設計されています。
- HTTP は拡張可能
- HTTP/1.0 で導入された HTTP ヘッダーによって、プロトコルの拡張や実験が容易になっています。
- HTTP はステートレスであるがセッションレスではない
- HTTP はステートレスです。同じコネクション上であっても、連続的に実行される 2 つのリクエスト間に関係性はありません。
- ステートフルとは、システムが現在の状態を表すデータなどを保持しており、その内容を処理に反映させる方式。
- しかし HTTP の核心がステートレスであっても、 HTTP Cookie によってステートフルなセッションを実現できます。
- HTTP はステートレスです。同じコネクション上であっても、連続的に実行される 2 つのリクエスト間に関係性はありません。
- HTTP とコネクション
- HTTP/1.1 でパイプライン (実装が難しいことが立証されました) や持続的接続を導入しました。 Connection ヘッダーを使用して、下層の TCP コネクションを部分的に制御できます。
- HTTP/2 はひとつのコネクションで複数のメッセージを多重化するように進化しました。コネクションををウォーム状態に保つのに役立ち、効率が向上します。
HTTP が制御できること
- キャッシュ
- サーバーはプロキシやクライアントに対して、何をどれだけの間キャッシュするかを指示できます。
- クライアントは中間のキャッシュプロキシに対して、保存されている文書を無視するよう指示できます。
- オリジン制約の緩和
- 同一オリジンのページだけが、ウェブページの情報すべてにアクセスできます。この制約はサーバーにとって負担になりますが、 HTTP ヘッダーでサーバー側の厳密な分割を緩和できます。
- 認証
- 基本的な認証は HTTP が提供しており、 WWW-Authenticate などのヘッダーを使用するか、 HTTP Cookie を使用した特別なセッションを設定する
- プロキシとトネリング
- プロキシサーバーとトンネリング
インターネットのさまざまなネットワークを移動するときに、プロキシサーバーと HTTP トンネルは、 World Wide Web 上のコンテンツへのアクセスを容易にしています。
プロキシには、フォワードプロキシ (またはトンネルまたはゲートウェイ) とリバースプロキシ (負荷分散、認証、復号化またはキャッシュ用のサーバーへのアクセスを制御および保護するために使用される) の2種類があります。HTTP トンネリング
トンネリングはデータをカプセル化することによって、パブリックネットワークを介してプライベートネットワークデータおよびプロトコル情報を送信します。
HTTP トンネリングは、より低いレベルのプロトコル (TCP) を伝送するために、より高いレベル (HTTP) のプロトコルを使用しています。
HTTP プロトコルは CONNECT と呼ばれるリクエストメソッドを指定します。リクエストされたリソースとの双方向通信を開始し、トンネルを開くために使用することができます。
- プロキシサーバーとトンネリング
- セッション
- HTTP Cookie を使用して、リクエストとサーバーのセッションを関連付けできます。これにより HTTP がステートレスプロトコルであるにもかかわらず、セッションを作成できます。
HTTP のフロー
クライアントがサーバー (最終目的地のサーバーまたは中間のプロキシ) と通信したいとき、クライアントは以下の段階を踏みます。
- TCP コネクションを開く: クライアントは新しいコネクションを開く、既存のコネクションを再使用する、あるいはサーバーに対して複数の TCP コネクションを開くことができます。
- HTTP メッセージを送信する
- サーバーから送信されたレスポンスを読み取る
- 次のリクエストのために、コネクションを閉じるか再使用する
HTTP パイプラインが有効である場合は、最初のレスポンスが完全に返るのを待たずに複数のリクエストを送信できます。
HTTP パイプラインは、 HTTP/2 でフレーム内にリクエストを強力に多重化する機能によって置き換えられました。
HTTP メッセージ
HTTP/1.1 以前の HTTP メッセージは、人間が読むことができます。 HTTP/2 ではこれらのメッセージがバイナリ構造のフレームに埋め込まれており、ヘッダーの圧縮や多重化といった最適化が可能になりました。本来の HTTP メッセージの部分だけがこのバージョンの HTTP で送信されていても、各メッセージの意味は変わっておらず、クライアントは本来の HTTP/1.1 メッセージを (事実上) 再構成します。したがって、 HTTP/2 メッセージを HTTP/1.1 形式で理解することは役に立ちます。
リクエスト
- HTTP メソッド
- 取り込むリソースのパス
- 状況から明らかであればリソースの URL はこの要素から取り除かれます。
- たとえばプロトコル (http://)、ドメイン (ここでは developer.mozilla.org)、TCP ポート (ここでは 80) が取り除かれます。
- HTTP プロトコルのバージョン
- サーバーに追加の情報を与える任意のヘッダー
レスポンス
- 準拠する HTTP プロトコルのバージョン。
- ステータスコード。
- リクエストが成功したか否か、およびその理由を示します。
- ステータスメッセージ。
- ステータスコードの簡単な説明ですが、権威はありません。
- リクエストと同様の HTTP ヘッダー。
- (省略可) リソースを含む本文。
HTTP キャッシュ
キャッシュは、提供されたリソースの複製を保存して、要求されたときに背後でその複製を提供する技術です。
ウェブキャッシュのストア内に要求されたリソースがあるとき、キャッシュはリクエストに介入して、提供元のサーバーから再びダウンロードする代わりにキャッシュ内の複製を返します。
キャッシュにはさまざまな種類があり、これらはプライベートキャッシュと共有キャッシュの 2 つのカテゴリーに大きく分類できます。
- 共有キャッシュ
- 複数のユーザーが再使用するためにレスポンスを保存するキャッシュ
- プライベートキャッシュ
- ひとりのユーザーのためのキャッシュ
HTTP キャッシュは必須ではありませんが、キャッシュしたリソースの再使用は通常望ましいことです。
ただし一般的な HTTP キャッシュはたいてい、GET のレスポンスのみキャッシュするよう制限されており、他のメソッドではキャッシュしません。
キャッシュ項目の一般的な形式は以下のとおり
- 取得要求に成功した結果: GET リクエストに対する 200 (OK) レスポンスには、HTML 文書、画像、ファイルなどのリソースが含まれています。
- 恒久的なリダイレクト: 301 (Moved Permanently) レスポンス。
- エラーレスポンス: 404 (Not Found) のページ。
- 不完全な結果: 206 (Partial Content) レスポンス。
- キャッシュのキーとして使用することが適切であると定義されていれば、GET 以外のレスポンス。
キャッシュを制御する
Cache-Control
ヘッダー
- キャッシュしない
Cache-Control: no-store
- キャッシュするが再検証する
- キャッシュした複製を渡す前に検証のため、キャッシュは生成元のサーバーにリクエストを送信します。
Cache-Control: no-cache
- キャッシュした複製を渡す前に検証のため、キャッシュは生成元のサーバーにリクエストを送信します。
- private キャッシュと public キャッシュ
- “public” ディレクティブは、どのキャッシュでもレスポンスを保存してよいことを示します。
これは、通常はキャッシュできない HTTP 認証やレスポンスステータスコードを伴うページをキャッシュしなければならない時に有用です。 - “private” はレスポンスがひとりのユーザーのためのものであり、共有キャッシュに保存してはならないことを示します。
Cache-Control: private Cache-Control: public
- “public” ディレクティブは、どのキャッシュでもレスポンスを保存してよいことを示します。
- 有効期限
- このヘッダーでもっとも重要なディレクティブが、リソースが陳腐化していないと考えられる最長期間を表す
"max-age=<seconds>"
ですCache-Control: max-age=31536000
- このヘッダーでもっとも重要なディレクティブが、リソースが陳腐化していないと考えられる最長期間を表す
- 検証
- “must-revalidate” ディレクティブを使用すると、キャッシュはリソースを使用する前に陳腐化の状態を検証しなければならず、また期限切れのリソースを使用するべきではありません。
Cache-Control: must-revalidate
- “must-revalidate” ディレクティブを使用すると、キャッシュはリソースを使用する前に陳腐化の状態を検証しなければならず、また期限切れのリソースを使用するべきではありません。
鮮度
リソースがキャッシュに保存されると、理論上は永久にキャッシュからリソースを提供することができます。
キャッシュは有限の記憶領域ですので、アイテムは定期的に記憶領域から削除されます。この処理はキャッシュ・エビクションと呼ばれます。
鮮度の寿命は、いくつかのヘッダーを基に計算されます。”Cache-Control: max-age=N” ヘッダーが指定された場合は、鮮度の寿命が N に等しくなります。
そうでない場合は
expirationTime = responseTime + freshnessLifetime - currentAge
のように計算される
Revving を適用したリソース
キャッシュされたリソースをより多く使用すると、ウェブサイトの応答性やパフォーマンスが向上するでしょう。この最適化のために、有効期限をできるだけ遠い未来にすることが推奨されています。この方法は定期的あるいはよく更新されるリソースでも使用できますが、まれにしか更新されないリソースでは問題があります。
ウェブ開発者は、Steve Souders 氏が revvingと呼ぶ技術を発明しました。あまり更新しないファイルは、特定の方法で命名します。その方法とは、通常はファイル名である URL にリビジョン (またはバージョン) 番号を追加することです。
この方法ではそれぞれの新しいリビジョンのリソースが変更されないリソースであるとみなされて、通常は 1 年あるいはそれ以上先の遠い未来を有効期限にすることができます。
キャッシュの検証
キャッシュされた文書の有効期限に達すると、検証または再取得を行います。キャッシュの検証は、サーバーが strong validator
または weak validator
を提供していれば実行できます。
ユーザーが再読み込みボタンを押すと、再検証が発生します。キャッシュされたレスポンスに “Cache-Control: must-revalidate” ヘッダーが含まれている場合は、通常のブラウジングでも再確認が発生します。
Cookieについて
HTTP Cookie (ウェブ Cookie、ブラウザー Cookie) は、サーバーがユーザーのウェブブラウザーに送信する小さなデータであり、ブラウザーに保存され、その後のリクエストと共に同じサーバーへ返送されます。
Cookie は主に、以下の 3 つの用途で使用されます。
- セッション管理
- パーソナライゼーション
- トラッキング
Cookie の作成
HTTP リクエストを受け取った後、サーバーはレスポンスで Set-Cookie ヘッダーを送信することができます。通常 Cookie はブラウザーに保存され、また Cookie は同じサーバーに対して行われるリクエストと共に HTTP の Cookie ヘッダーの中で送信されます。
単純な Cookie は次のように設定されます。
Set-Cookie: <cookie-name>=<cookie-value>
また、そのサーバーへのその後のすべてのリクエストにおいて、ブラウザーは以前格納されたすべてのクッキーを、 Cookie ヘッダーを使用してサーバーへ送信します。
Cookie の持続時間の定義
Cookie の持続時間は2通りの方法で定義することができます。
- セッション Cookie は現在のセッションが終了すると削除されます。
- 持続的 Cookie は、 Expires 属性で指定された時刻、または Max-Age で指定された期間が経過した後に削除されます。
Cookie へのアクセス制限
クッキーが安全に送信され、意図しない第三者やスクリプトからアクセスされないようにするには、 Secure 属性
と HttpOnly 属性
の2つの方法があります。
- Secure 属性がついたクッキーは HTTPS プロトコル上の暗号化されたリクエストでのみサーバーに送信され、安全でない HTTP では決して送信されないため、中間者攻撃者が簡単にアクセスすることはできません。(URL に http: を含む) 安全でないサイトは、 Secure 属性を使用してクッキーを設定することができません。
- HttpOnly 属性を持つクッキーは、JavaScript の Document.cookie API にはアクセスできません。サーバーに送信されるだけです。例えば、サーバー側のセッションを持続させるクッキーは JavaScript が利用する必要はないので、 HttpOnly 属性をつけるべきです。この予防策は、クロスサイトスクリプティング (XSS) 攻撃を緩和するのに役立ちます。
Cookie の送信先の定義
Domain および Path 属性は、Cookie のスコープ、つまり Cookie を送信する対象の URL を定義します。
Domain 属性
- Domain 属性は、Cookie を受信することができるホストを指定します。指定されていない場合は、既定でクッキーを設定したのと同じオリジンとなり、サブドメインは除外されます。 Domain が指定された場合、サブドメインは常に含まれます。したがって、 Domain を指定すると省略時よりも制限が緩和されます。
Path 属性
- Path 属性は、 Cookie ヘッダーを送信するためにリクエストされた URL の中に含む必要がある URL のパスを示します。 例えば、Path=/docs を設定すると、以下のパスに一致します。
/docs /docs/Web/ /docs/Web/HTTP
SameSite 属性
- SameSite 属性により、サーバーがオリジン間リクエスト (ここでサイトは登録可能なドメインによって定義されます) と一緒にクッキーを送るべきではないことを要求することができます。
- これは、クロスサイトリクエストフォージェリ攻撃 (CSRF) に対していくらかの防御となります。
セキュリティ
情報をクッキーに保存するときは、すべてのクッキーの値がエンドユーザーから見え、変更できることを理解しておいてください。アプリケーションによっては、サーバー側で検索される不透明な識別子を使用するか、 JSON ウェブトークンのような代替の認証/機密性メカニズムを調べたほうが良いかもしれません。 クッキーへの攻撃を緩和する方法には次のようなものがあります。
- HttpOnly 属性を使用して、 JavaScript からクッキーの値にアクセスすることを防ぎます。
- 機密情報 (認証を示す場合など) のために使用されたクッキーは、持続時間を短く、 SameSite 属性を Strict または Lax に設定してください。
トラッキングとプライバシー
Cookie はドメインに関連付けられます。このドメインが閲覧しているページのドメインと同じである場合、そのクッキーは、ファーストパーティ Cookie と呼ばれます。
ドメインが異なる場合は、サードパーティ Cookie と呼びます。
Cookie に関する規制
クッキーの使用を対象とした法規制には、以下のようなものがあります。
- EU の 一般データ保護規則 (GDPR)
- EU の ePrivacy 指令
- カリフォルニア州消費者プライバシー法
これらの規制の要件には次のようなものがあります。
- サイトがクッキーを使用することをユーザーに通知する。
- ユーザーが一部またはすべてのクッキーをオプトアウトできるようにする。
- ユーザーがクッキーを受け取らなくても、サービスの大部分を利用できるようにする。
オリジン間リソース共有 (CORS)
オリジン間リソース共有 (Cross-Origin Resource Sharing, CORS) は、追加の HTTP ヘッダーを使用して、あるオリジンで動作しているウェブアプリケーションに、異なるオリジンにある選択されたリソースへのアクセス権を与えるようブラウザーに指示するための仕組み
ウェブアプリケーションは、自分とは異なるオリジン (ドメイン、プロトコル、ポート番号) にあるリソースをリクエストするとき、オリジン間 HTTP リクエストを実行します。
セキュリティ上の理由から、ブラウザーは、スクリプトによって開始されるオリジン間 HTTP リクエストを制限しています。例えば、 XMLHttpRequestや Fetch API は同一オリジンポリシー (same-origin policy) に従います。つまり、これらの API を使用するウェブアプリケーションは、そのアプリケーションが読み込まれたのと同じオリジンに対してのみリソースのリクエストを行うことができ、それ以外のオリジンからの場合は正しい CORS ヘッダーを含んでいることが必要です。
アクセス制御シナリオの例
単純リクエストは、以下のすべての条件を満たすもの
- 許可されているメソッドのうちのいずれかであること。
- GET
- HEAD
- POST
- Fetch 仕様書で CORS セーフリストリクエストヘッダーとして定義されている以下のヘッダーだけです。
- Accept
- Accept-Language
- Content-Language
- Content-Type
プリフライトリクエスト
単純リクエストとは異なり、「プリフライト」リクエストは始めに OPTIONS メソッドによる HTTP リクエストを他のドメインにあるリソースに向けて送り、実際のリクエストを送信しても安全かどうかを確かめます。
サイト間リクエストがユーザーデータに影響を与える可能性があるような場合に、このようにプリフライトを行います。
単純リクエスト → 無条件にリクエストを送信でき、レスポンスヘッダーによって、JavaScriptがリソースを受け取れるかを判断する。
単純リクエスト以外 → プリフライトリクエスト(Preflight)で事前にリクエスト送信の許可を確認して、その後にリクエストを送信する
【CORS】JavaScriptにおけるCORSやPreflightを理解する
資格情報を含むリクエスト
XMLHttpRequest や Fetch と CORS の両方によって明らかになる最も興味深い機能は、HTTP クッキーと HTTP 資格情報によってわかる「資格情報を含む」リクエストを作成することができることです。既定では、サイト間の XMLHttpRequest または Fetch の呼び出しにおいて、ブラウザーは資格情報を送信しません。 XMLHttpRequest オブジェクトまたは Request のコンストラクターの呼び出し時に、特定のフラグを設定する必要があります。
資格情報付きリクエストに返答する場合、
- サーバーは Access-Control-Allow-Origin ヘッダーで
"*"
ワイルドカードを指定してはならず、 Access-Control-Allow-Origin: https://example.com のように、明示的にオリジンを指定しなければなりません。 - サーバーは Access-Control-Allow-Headers ヘッダーで
"*"
ワイルドカードを指定してはならず、 Access-Control-Allow-Headers: X-PINGOTHER, Content-Type のように、明示的にヘッダー名を指定しなければなりません。 - サーバーは Access-Control-Allow-Methods ヘッダーで
"*"
ワイルドカードを指定してはならず、 Access-Control-Allow-Methods: POST, GET のように、明示的にメソッド名を指定しなければなりません。
HTTP レスポンスヘッダー
Access-Control-Allow-Origin
Access-Control-Allow-Origin は、リソースへのアクセスを許可するオリジンをブラウザーに伝えるための単一のオリジン、または — 資格情報を含まないリクエストにおいては — どのオリジンにもリソースへのアクセスを許可することをブラウザーに伝えるワイルドカード "*"
のどちらかを指定することができます。
Access-Control-Allow-Origin: https://mozilla.org
Vary: Origin
サーバーがワイルドカード "*"
ではなく (ホワイトリストの一部としてリクエストするオリジンに基づいて動的に変更される可能性がある) 単一のオリジンを指定した場合は、サーバーは Origin を Vary レスポンスヘッダーに含めて、サーバーのレスポンスが Origin リクエストヘッダーの値によって変化することもクライアントに示してください。
Access-Control-Expose-Headers
指定されたヘッダーをブラウザー内の JavaScript (getResponseHeader() など) からアクセスできる許可リストに加えます。
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
Access-Control-Max-Age
このヘッダーはプリフライトリクエストの結果をキャッシュしてよい時間を示します。
Access-Control-Max-Age: <delta-seconds>
delta-seconds 引数は、結果をキャッシュしてよい時間を秒単位で示します。
Access-Control-Allow-Credentials
credentials フラグが true である場合に、リクエストへのレスポンスを開示してよいか否かを示します。プリフライトリクエストのレスポンスの一部として用いられたときは、実際のリクエストで資格情報を使用してよいか否かを示します。単純な GET リクエストはプリフライトを行いませんので、リソースへのリクエストが資格情報付きで行われた場合にリソースと共にこのヘッダーを返さなければ、ブラウザーがそのレスポンスを無視し、ウェブのコンテンツが返されないことに注意してください。
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods
リソースへのアクセス時に許可するメソッドを指定します。これはプリフライトリクエストのレスポンスで用いられます。
Access-Control-Allow-Headers
プリフライトリクエストへのレスポンスで使用され、実際のリクエストを行う際に使用される HTTP ヘッダーを示します。
HTTP リクエストヘッダー
HTTP リクエストを発行する際、オリジン間リソース共有機能を利用するためにクライアントが使用できるヘッダー
Origin
Origin ヘッダーはサイト間のアクセスリクエストやプリフライトリクエストのオリジンを示します。
origin は、リクエストを開始したサーバーを示す URL です。ここにパス情報は含めず、サーバー名だけにします。
Note: origin の値は null にすることができます。
なお、すべてのアクセス制御リクエストにおいて、 Origin ヘッダーは常に送信されます。
Access-Control-Request-Method
プリフライトリクエストを発信する際に、実際のリクエストを行う際に使用する HTTP メソッドをサーバーに知らせるために使用します。
Access-Control-Request-Headers
プリフライトリクエストを発行する際に、実際のリクエストを行う際に (setRequestHeader() などによって) 使用する HTTP ヘッダーをサーバーに知らせるために使用します。
参考資料
HTTP の進化
初期段階で使用された HTTP プロトコルはとてもシンプルであり、後に HTTP/0.9 と名付けられ、また時にはワンラインプロトコルとも呼ばれました。
初期バージョンの HTTP にはバージョン番号がありませんでした。以降のバージョンと区別するため、後に 0.9 と呼ばれるようになりました。
HTTP/0.9 はとても単純です。リクエストは唯一使用可能なメソッドである GET で始まって、リソースへのパス (サーバーに接続すればプロトコル、サーバー名、ポートが必要ではなくなるため、 URL ではありません) が後に続く 1 行で構成されます。
GET /mypage.html
HTTP/1.0 – 拡張性を築く
HTTP/0.9 は制約がとても多いため、多目的に使用できるようにブラウザーやサーバーがいち早く拡張しました。
- 各リクエストでバージョン情報を送信するようになりました
- レスポンスの始めにステータスコードの行を送信し、ブラウザー自体がリクエストの成功または失敗を理解して、その結果に応じて動作を順応できる
- リクエストとレスポンスの両方で HTTP ヘッダーの概念を導入して、メタデータの送信を可能にするとともにプロトコルを極めて柔軟かつ拡張可能にしました。
- 新たな HTTP ヘッダーの助けを借りて、プレーン HTML ファイル以外の文書を転送する機能を追加する
リクエストとレスポンスは以下のように進化した
GET /mypage.html HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)
200 OK
Date: Tue, 15 Nov 1994 08:12:31 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/html
<HTML>
A page with an image
<IMG SRC="/myimage.gif">
</HTML>
HTTP/1.1 – 標準化されたプロトコル
HTTP/1.1 ではあいまいな部分を明確にするとともに、いくつもの改良を施しました:
- コネクションの再利用が可能になりました。
- パイプライン機能を追加しました。
- Chunked 形式のレスポンスをサポートしました。
- 新たなキャッシュ制御の仕組みを追加しました。
- 言語、エンコーディング、タイプのコンテンツネゴシエーションを導入しました。
- 同じ IP アドレスで異なるドメインを提供できる Host ヘッダーのおかげで、サーバーのコロケーションが可能になりました。
HTTP/2 – 高パフォーマンスなプロトコル
長年かけてウェブページはより複雑になり、アプリケーション自体も同様に複雑化しました。
HTTP/1.1 のコネクションは、正しい順序でリクエストを送信しなければなりません。理論上は並行していくつかのコネクションを使用できます (通常 5 から 8 の間) が、かなりのオーバーヘッドや複雑性をもたらします
HTTP/2 プロトコルには、HTTP/1.1 との大きな違いがいくつかあります:
- テキスト形式ではなく、バイナリ形式のプロトコルです。
- 多重化されたプロトコルです。同じコネクションでリクエストを並行して扱うことができ、HTTP/1.x プロトコルの順序やブロッキングの制約を排除しています。
- ヘッダーを圧縮します。
- サーバープッシュと呼ばれる仕組みによって、リクエストより先にサーバーがクライアントのキャッシュにデータを加えることができます。
HTTP メッセージ
HTTP メッセージは、サーバーとクライアントがデータを交換する手段です。
HTTP メッセージは ASCII でエンコードされたテキスト情報で構成されており、複数の行にまたがります。
HTTP のリクエストやレスポンスは似た構造を共用しており、以下の要素で構成されます。
- 実行するリクエスト、または成功か失敗かの状態を表す開始行。開始行は常に 1 行です。
- リクエストの詳細を示す、またはメッセージに含まれる本文を説明する、省略可能な HTTP ヘッダー一式。
- リクエストのメタ情報がすべて送信されたことを示す空行。
- リクエストに関連付けられたデータ (HTML フォームの内容など)、あるいはレスポンスに関連付けられたドキュメントを含む、省略可能な本文。
HTTP リクエスト
HTTP リクエストは、アクションを始めるためにクラアントからサーバーへ送られます。その開始行には、3 つの要素が含まれています。
- HTTP メソッド。実行するアクションを表わす動詞 (GET、PUT、POST など) または名詞 (HEAD、OPTIONS)。
- リクエスト対象。通常は URL ですが、プロトコル、ポート、ドメインの絶対パスは通常、リクエストの状況から明らかにされます。
- HTTP バージョン。これはメッセージの残りの部分の構造を定義しており、レスポンスで使用することを想定しているバージョンを示す役割もあります。
リクエストの HTTP ヘッダー は、HTTP ヘッダーの一定の基本構造に従います。大文字・小文字を区別しない文字列の後にコロン (‘:’) と、ヘッダーに応じた構造の値が続きます。
使用できるリクエストヘッダーは多数あります。これらはいくつかのグループに分類されます。
- 一般ヘッダーは、 Via など、メッセージ全体に適用されるものです。
- リクエストヘッダーは、 User-Agent, Accept-Type, 指定するとリクエストを変更するもの (Accept-Language など)、状況を示すもの (Referer など)、条件を与えるもの (If-None など) があります。
- エンティティヘッダーは Content-Length など、リクエストの本文に適用されます。
リクエストの最後の部分が本文です。本文が存在しないリクエストもあります。
本文は、大きく 2 種類に分類されます。
- 単一リソースの本文。1 個のファイルで構成され、Content-Type と Content-Length の 2 つのヘッダーで定義されます。
- 複数リソースの本文。マルチパートの本文で構成され、それぞれが異なる情報を持ちます。これは主に、 HTML フォームと関連付けられます。
HTTP レスポンス
HTTP レスポンスの開始行はステータス行と呼ばれ、以下の情報を持ちます。
- プロトコルバージョン。通常 HTTP/1.1 です。
- ステータスコード。リクエストが成功したか失敗したかを示します。一般的なステータスコードは 200, 404, 302 です。
- ステータス文字列。手短な単なる情報ですが、人間が HTTP メッセージを理解するのを助けるために、ステータスコードをテキストで説明します。
レスポンスの HTTP ヘッダーは、他のヘッダーと同様に一定の基本構造に従います。
レスポンスの最後の部分が本文です。本文を持たないレスポンスもあります。
201 Created や 204 No Content といったステータスコードのレスポンスは通常、本文がありません。
本文は、大きく 3 種類に分類されます。
- 大きさが判明している 1 個のファイルで構成される、単一リソースの本文。 Content-Type と Content-Length の 2 つのヘッダーで定義されます。
- 大きさが不明な 1 個のファイルで構成される、単一リソースの本文。 Transfer-Encoding を chunked に設定して、 chunked 形式でエンコードされます。
- 複数リソースの本文。マルチパートの本文で構成され、それぞれが異なる情報のセクションを持ちます。これは比較的まれです。
HTTP/2 フレーム
HTTP/1.x のメッセージには、パフォーマンスの欠点があります。
- ヘッダーは本文と異なり、圧縮されません。
- あるメッセージと次のメッセージでヘッダーが酷似していることがよくありますが、それでも複数のコネクションにわたって繰り返されます。
- 多重化することができません。同じサーバーに対して複数のコネクションを開かなければなりません。
HTTP/2 は次の段階に進みました。 HTTP/1.x のメッセージを、ストリーム内に埋め込まれるフレームに分割します。データのフレームとヘッダーのフレームは区別され、ヘッダーの圧縮が可能になります。多重化と呼ばれる処理によって複数のストリームがまとめられ、下層の TCP コネクションの効率を向上させることができます。
典型的な HTTP セッション
HTTP のようなクライアントサーバープロトコルでは、セッションが 3 つの段階で構成されます。
- クライアントは TCP コネクション (トランスポート層が TCP ではない場合は、他の適切なコネクション) を確立します。
- クライアントは要求を送り、回答を待ちます。
- サーバは要求を処理して、ステータスコードや適切なデータを提供する回答を返信します。
クライアントサーバープロトコルでは、クライアントがコネクションを確立します。HTTP のコネクションを開くとは、下層のトランスポート層のコネクションを確立することであり、これは通常 TCP です。
クライアントの要求の送信
コネクションを確立すると、ユーザーエージェントは要求を送信できます (ユーザーエージェントは一般的にウェブブラウザーですが、ほかにもクローラーなどがあります)。クライアントの要求は CRLF (キャリッジリターンに続いてラインフィード) で区切られたテキストのディレクティブで構成され、3 つのブロックに分けられます。
- 最初の行は、要求メソッドの後に次の引数が続きます。
- 文書のパス、すなわち絶対 URL からプロトコル名とドメイン名を除いたものです。
- HTTP プロトコルのバージョン。
- 後続の行は HTTP ヘッダーであり、サーバーに対してどの種類 (例えば、言語や MIME タイプ) のデータが適切かを示す情報や、サーバーの動作を変える (例えば、すでにキャッシュされている場合は回答を送信しない) データを与えます。
- 最後のブロックは省略可能なデータブロックで、主に POST メソッドで使用される追加のデータを含みます。
サーバー応答の構造
接続したエージェントが要求を送信すると、ウェブサーバーはその要求を処理して、最終的に応答を返信します。クライアントの要求と同様にサーバーの応答はテキストのディレクティブで構成され、それらは CRLF で区切られており、3 つのブロックに分けられます:
- 最初の行はステータス行で、受け入れた HTTP バージョンとステータス要求で構成されます (そして、人間に読めるテキストで意味を簡単に示します)。
- 後続の行はそれぞれ具体的な HTTP ヘッダーを表しており、クライアントに対して送信したデータに関する情報 (例えば種類、サイズ、圧縮方法、キャッシュ情報) を与えます。クライアントの要求の HTTP ヘッダーブロックと同様に、これらの HTTP ヘッダーも空行で終わるブロックを構成します。
- 最後のブロックはデータブロックで、任意のデータを含みます。
HTTP/1.x のコネクション管理
HTTP/1.x では短命なshort-livedコネクション、持続的なpersistentコネクション、HTTP パイプラインといったモデルがあります。
HTTP は、クライアントとサーバーの間のコネクションを提供するトランスポートプロトコルを、たいてい TCP に依存しています。
HTTP/1.1 で、新たなモデルを 2 種類導入しました。持続的なコネクションモデルは、連続したリクエストの間はコネクションを開き続けておくことで、新たなコネクションを開始するために必要な時間を削減します。 HTTP パイプラインモデルはさらに 1 歩進んで、回答を待っている間も複数の連続したリクエストを送信して、ネットワークの遅延の大部分を削減します。
短命なコネクション
短命なコネクションは HTTP の初期のモデルであり、 HTTP/1.0 の既定のモデルです。
それぞれの HTTP リクエストは、自身のコネクションで完結します。
短命なコネクションは TCP の効率化機能を使用しておらず、また新たなコールド状態のコネクションで送信することに固執するため最適な状態よりもパフォーマンスが低下します。
持続的なコネクション
短命なコネクションには、大きな問題点が 2 つあります。
- 新しいコネクションを確立するためにかなり時間がかかること、
- および下層の TCP コネクションは何度も使用するとき (ウォーム状態のコネクション) しかパフォーマンスが向上しないこと
持続的なコネクションはしばらくの間開かれたままであり、いくつかのリクエストで再使用できます。よって新たな TCP ハンドシェイクにかかる時間を節約して、 TCP のパフォーマンス向上機能を活用します。
持続的な接続には欠点もあります。アイドル状態でもサーバーのリソースを消費しており、高負荷状態では DoS 攻撃 を招く可能性があります。
*HTTP パイプラインは、現代のブラウザーでは既定で有効化されていません。
ドメインシャーディング
ドメインシャーディングというのは、HTML や画像、JavaScript などを複数ドメインから配信する高速化手法
特別に必要である場合を除き、この非推奨の技術は使用しないでください。代わりに、 HTTP/2 に切り替えてください。
HTTP/2 では、ドメインシャーディングは有用ではありません。 HTTP/2 のコネクションは、並行した優先度がないリクエストをより上手に扱うことができます。