HTTP ルーティング
最終更新日 2025年04月17日(木)
Table of Contents
- レガシールーターの廃止と EOL
- レガシールーターと Router 2.0
- Router 2.0 の有効化と無効化
- ルーティング
- リクエストの分散
- リクエストの並列性の制限
- Common Runtime での dyno の接続動作
- Private Spaces での dyno の接続動作
- タイムアウト
- キープアライブ
- 同時接続
- リクエストのバッファリング
- 応答のバッファリング
- Heroku のヘッダー
- Heroku ルーターのログ形式
- キャッシング
- WebSocket
- gzip で圧縮された応答
- サポートされている HTTP メソッド
- Expect: 100-continue
- サポートされている HTTP のバージョン
- Router 2.0 と HTTP/2
- HTTP の検証と制限
- プロトコルのアップグレード
- サポート対象外
- Common Runtime で使用可能な暗号スイート
- Private Spaces で利用可能な暗号スイート
Heroku プラットフォームでは、アプリのホスト名に送信された HTTP リクエストが自動的に Web dyno にルーティングされます。異なるランタイムでは異なるルーターが使用されます。
Common Runtime には、レガシールーターと Router 2.0 のオプションがあります。レガシールーターは廃止予定であり、2025 年春にサポート終了が予定されています。この記事では、ルーターの動作や HTTP 仕様への準拠について詳しく説明しています。
Private Spaces Runtime のどちらの世代も Common Runtime とは異なるルーターが使用されていますが、このページで説明されているルーターとほぼ同じように動作します。特に Fir Private Spaces のルーターは Router 2.0 と非常によく似ていますが、まったく同じではありません。Private Spaces Runtime ルーターの違いについては、「Private Spaces でのルーティング」を参照してください。
レガシールーターの廃止と EOL
Common Runtime のレガシールーターは廃止予定です。現在、Heroku はdyno 層に基づいて、Common Runtime アプリケーションを新しいルーターに少しずつ移行しています。Common Runtime では、Eco、Basic、Standard、Performance 層のみが利用可能です。2025 年 4 月初旬時点で、すべての Eco 層アプリケーションは自動的に Router 2.0 にルーティングされます。これには、既存の Eco 層アプリケーションと新しく作成された Eco 層アプリケーションが含まれます。完全な移行スケジュールについては、「Legacy Router End-of-Life FAQ」(レガシールーターのサポート終了に関する FAQ) を参照してください。
Cedar と Fir の両方の世代を含む Private Spaces は、すでに Router 2.0 に類似した最新のルーターを実行しています。そのため、Private Spaces のアプリケーションはルーター移行の対象外です。
レガシールーターと Router 2.0
Common Runtime スタック上のすべてのアプリケーションのエントリポイントは、Web dyno への直接ルーティングパスを提供する herokuapp.com
ドメインです。Heroku で現在利用可能な Common Runtime ルーターは、新しい Router 2.0 とレガシールーターの 2 つです。Router 2.0 はレガシールーターを超える追加機能 (HTTP/2 など) をサポートします。
基本的なルーティングやほとんどの機能は 2 つのルーター間で同じですが、相違点と拡張機能についてはこのドキュメントの関連セクションで説明しています。レガシールーターの代わりに Router 2.0 を使用することをお勧めします。レガシールーターは廃止予定であり、今後新しい機能は追加されません。サポート終了と移行プランについての詳細は、こちらのヘルプ記事を参照してください。
レガシールーターと Router 2.0 は次の機能をサポートしています。
機能 | レガシールーター | Router 2.0 | 注意事項 |
---|---|---|---|
ルーティング | x | x | TLS ターミネーションを含む基本的なルーティングがサポートされています。 |
ルーターログ | x | x | リクエストごとのルーターログがアプリのログストリームに表示されます。 |
エラーコード | x | x | ほとんどの H コードはサポートされており、ルーターのログに記録されます。現在、例外は H23 と H26 のみとなっています。 |
dyno のスリープ | x | x | Eco dyno では dyno のスリープ (アイドル状態) がサポートされています。 |
リクエストの同時実行の制限 | x | x | 各ルーターはアプリごとの内部リクエストカウンターを保持し、アプリごとの同時リクエスト数を制限します。 |
Heroku のヘッダー | x | x | Heroku のヘッダーは、Heroku が HTTP 応答に追加するヘッダーのセットです。 |
WebSocket | x | x | WebSocket プロトコルは両方のルーターでサポートされています。 |
Expect 100-continue | x | x | Expect: 100-continue ヘッダーは両方のルーターでサポートされています。 |
dyno の隔離 | x | x | ルーターは到達不可能な dyno を隔離します。 |
Preboot | x | x | Preboot リリース動作は両方のルーターで機能します。 |
セッションアフィニティ | x | x | エンドユーザーからのすべての HTTP リクエストを Web dyno に関連付けます。 |
HTTP/2 | x | HTTP/2 は Router 2.0 でのみ使用できます。 | |
キープアライブ | x | Router 2.0 のみが、ルーターと dyno 間の HTTP キープアライブをサポートします。 | |
IPV6 | Router 2.0 にのみ追加される予定です。 |
Router 2.0 の有効化と無効化
新しいルーターを使用するには、次の機能を有効にします。
$ heroku features:enable http-routing-2-dot-0 -a <app name>
この機能を有効にすると、アプリは Router 2.0 を介してトラフィックを受信するようになります。
http-routing-2-dot-0
フラグを有効または無効にした後、クライアントはアプリへの接続を再確立する必要があります。
Router 2.0 の使用を停止するには以下を実行します。
Eco 層アプリで Router 2.0 を無効にすることはできません。
$ heroku features:disable http-routing-2-dot-0 -a <app name>
アプリを以前のルーティング動作に戻すために必要な作業は、他にはありません。
ルーティング
インバウンドリクエストは、SSL ターミネーションを提供するロードバランサーによって受信されます。リクエストは、ここから一連のルーターに直接渡されます。
ルーターは、アプリケーションの Web dyno の場所を判断し、これらの dyno のいずれかに HTTP リクエストを転送する役割を担います。
Heroku インフラストラクチャ経由でのエンドクライアントからアプリケーションへのリクエストの非難読化されたパスにより、チャンク化された応答、長いポーリング、WebSocket、非同期 Web サーバーを使用した 1 つの Web プロセスからの複数の応答の処理などの HTTP 1.1 の機能を完全にサポートできます。](#http-2-with-router-2-0)Heroku では [Router 2.0 を介して HTTP/2 をサポートしています。HTTP 1.0 との互換性も維持しています。
リクエストの分散
ルーターは、ランダム選択アルゴリズムを使用して、Web dyno 間で HTTP リクエストのバランシングを行います。多数の dyno がある場合は、このアルゴリズムによって、選択を行うルーターと同じ AWS 可用性ゾーンに存在する dyno に対する選択に任意でバイアスがかけられる場合があります。
リクエストの並列性の制限
各ルーターでは、内部のアプリごとのリクエストカウンターが維持されます。Common Runtime では、ルーターでアプリごとの同時リクエスト数が制限されます。ただし、ルーター間での調整は行われないため、このリクエストの制限はルーターごとになります。各ルーターのリクエストカウンターの最大サイズは 200n です (n は、アプリで実行中の Web dyno の数)。特定のルーターのリクエストカウンターがいっぱいになると、そのルーターへのその後のリクエストは、すぐに H11 (バックログが深すぎます) の応答を返します。
Common Runtime での dyno の接続動作
Heroku が HTTP リクエストを受信すると、ルーターは、Common Runtime で実行中のランダムに選択された Web dyno への新しいアップストリーム TCP 接続を確立します。dyno が接続を拒否するか、5 秒後に接続を確立できない場合は、dyno を隔離します。そのルーターは最大 5 秒間、dyno にリクエストを転送しなくなります。隔離は、1 つのルーターにのみ適用されます。隔離されている dyno のリストは各ルーターで独自に管理されるため、ほかのルーターは、その dyno に接続を転送し続ける可能性があります。
接続が拒否されるかタイムアウトになると、リクエストを処理しているルーターは別の dyno で接続を再試行します。接続試行は最大 10 回まで行われますが、実行中の Web dyno が 10 個未満の場合は、試行回数は少なくなります。接続を確立できない場合、ルーターは「H19: Connection Timeout」 (H19: 接続タイムアウト) または「H21: Connection Refused」 (H21: 接続が拒否されました) のエラーを返します。
すべての dyno が隔離された場合、ルーターはインクリメンタルなバックオフを使用して最大 75 秒間、隔離されていない dyno の検索を再試行します。ルーターは隔離されていない dyno を見つけた場合、接続を確立しようとします。隔離されていない dyno を見つけられない場合、75 秒後にルーターは 503 で応答し、H99 エラーを返します。
接続を確立しようとする場合、リクエストの合計タイムアウトは 75 秒です。
Private Spaces での dyno の接続動作
Private Space の dyno は、専用のネットワークおよびルーティングレイヤーで実行され、プライベートネットワーク上で相互に通信します。Private Space のルーターは、許可されている一連の固定 IP アドレス上でアウトバウンド HTTP リクエストを受信します。信頼できる IP または Internal Routing を使用して、受信 Web リクエストを制限することもできます。
カスタムの信頼できる IP と Internal Routing は、Fir 世代のアプリとスペースではまだ利用できません。これらの機能が追加されたときに通知を受け取るには、Changelog の受信登録を行ってください。
Common Runtime の Web dyno でのルーターの動作とは異なり、Private Space のルーターは、接続が拒否されるかタイムアウトになった場合に、ある Web dyno から別の Web dyno に HTTP リクエストの接続を転送しません。代わりに、30 秒後に接続がタイムアウトになり、H12 を返します。Private Space でのルーティング動作の詳細については、「Routing in Private Spaces」(Private Space でのルーティング) の記事を参照してください。
タイムアウト
dyno の接続が確立された後、HTTP リクエストの最初の 30 秒間の期間内に、Web プロセスが応答データ (完了済みの応答、またはプロセスがアクティブであることを示す一定量の応答データ) を返す必要があります。最初の 30 秒間の期間内に応答データを返さないプロセスについては、ログに H12 エラーが表示されます。
初回応答の後、(クライアントから、またはアプリのプロセスから) 送信されるバイトごとに繰り返しの 55 秒間の期間がリセットされます。この 55 秒間の期間中にデータが送信されないと、接続が切断され、H15 または H28 エラーがログに記録されます。
詳細は、「Request Timeout」(リクエストのタイムタウト) の記事を参照してください。
キープアライブ
Router 2.0 と Fir ルーターは、ルーター自身と dyno の間でキープアライブ接続を使用します。一方、レガシールーターはキープアライブ接続をサポートしておらず、各リクエスト後に接続を閉じます (WebSocket 接続を除く)。
Router 2.0 と Fir ルーターはどちらも、ルーター自身とアプリケーションの Web dyno との間で接続プールを維持します。現在 HTTP リクエストや WebSocket 接続を処理していない接続の場合、アイドルタイムアウトは 90 秒です。このタイムアウトに達すると、ルーターは警告なしで接続を閉じ、Heroku のエラーコードは報告されません。
ルーターと dyno 間の接続の再利用を無効にするには、http-disable-keepalive-to-dyno
labs フラグを使用できます。Fir 世代のアプリでは、キープアライブを無効にすることはできません。
同時接続
herokuapp.com
のルーティングスタックでは、Web dyno への多数の同時接続が可能です。本番アプリについては、アプリの応答性を最大化するために、複数の同時接続が可能な内蔵 Web サーバーを常に選択します。長いポーリングのリクエストについても、同時接続を利用できます。
ほとんどすべての最新の Web フレームワークと内蔵可能な Web サーバーは、複数の同時接続をサポートしています。dyno でのリクエストの同時処理が可能な Web サーバーの例として、Unicorn (Ruby)、Goliath (Ruby)、Puma (JRuby)、Gunicorn (Python)、および Jetty (Java) があります。
リクエストのバッファリング
受信リクエストを処理するときに、ルーターはバッファを設定して、HTTP リクエスト行全体とリクエストヘッダーを受信します。リクエスト行とヘッダーのそれぞれに、「HTTP の検証と制限」で詳細に記載されている制限があります。content-length (コンテンツ長) が明確に定義されているリクエストの本体は、継続的に入力とフラッシュが繰り返される 1024 バイトのバッファを使用して伝送されます。このサイズは、容量面で、リクエストの大半に対応できるサイズです。データが着信すると、ストリーミングされるリクエスト本体 (チャンクエンコーディング) が通過します。HTTP ヘッダー一式が受信されると、初めてリクエストの dyno へのディスパッチが開始されます。
結果として、それぞれのルーターがすべてのリクエストのヘッダーセクションをバッファリングし、Heroku の内部ネットワークの実行と同じ速さで dyno に配信します。リクエスト本体を読み込む必要があるまで、dyno は低速のクライアントから保護されます。リクエストの本体を低速で伝送するクライアントから保護する必要がある場合は、dyno で接続を終了してリクエストをドロップするタイミングを決定するために利用できるリクエストヘッダーを設定します。
応答のバッファリング
ルーターは、1 つの接続あたりの dyno からの応答に対して、1 MB のバッファを維持します。つまり、クライアントが応答を受信する速度が dyno に影響するまでに最大 1 MB のサイズの応答を送信できます。dyno が接続を終了しても、ルーターは応答のバッファをクライアントに送信し続けます。1 MB のバッファサイズより大きい応答の転送レートは、クライアントがデータを受信できる速度に制限されます。
Heroku のヘッダー
HTTP 仕様のとおり、すべてのヘッダーは大文字と小文字が区別されるものと見なされます。X-Forwarded-For
、X-Forwarded-By
、X-Forwarded-Proto
、X-Forwarded-Host
の各ヘッダーは、既存のフィールドが追加された順番を識別することができないため、セキュリティ上の理由から信頼されません (Forwarded HTTP Extension)のとおり)。
X-Forwarded-For
: Heroku ルーターに接続しているクライアントの発信元の IP アドレス。Heroku ルーターがX-Forwarded-For
ヘッダーをすでに含むリクエストを受信した場合、ルーターが検出した発信元 IP はリストの右側に追加されます。X-Forwarded-Proto
: HTTP リクエストの発信元のプロトコル (例: https)X-Forwarded-Port
: HTTP リクエストの発信元のポート (例: 443)X-Request-Start
: リクエストがルーターに受信されたときの UNIX タイムスタンプ (ミリ秒)X-Request-Id
: Heroku の HTTP リクエスト IDVia
: Heroku ルーターのコードネーム
ネットワークエラーのログ記録
Heroku はネットワークエラーのログ記録 (NEL) を経由して選択したユーザーのブラウザデータを収集します。NEL は、インシデントが顧客に悪影響を及ぼし始める前にアクションを実行するための詳細な情報を Heroku に提供します。NEL は W3C 標準 (ドラフト) であり、エンドユーザーのデータを使用して Web アプリケーションのパフォーマンス特性をリアルタイムで測定する方法を定義します。
Google Chrome や Microsoft Edge など、いくつかのブラウザがこの標準を実装しています。サーバー上では、情報を集約して接続とレイテンシーのメトリクスを処理するエンドポイントにレポートを送信するようブラウザに指示するヘッダーで、HTTP 応答に注釈を付ける機能があります。Heroku は、Salesforce のプライバシーに関する声明に従って NEL を実施します。
Heroku は HTTP 応答に次のヘッダーを追加します。
NEL
: ネットワークリクエストのログ記録を設定するために使用されるヘッダーReport-To
: このフィールドは、ユーザーエージェントに起点のレポートエンドポイントを保存するように指示しますReporting-Endpoints
: リソースのレポート設定を構成するために使用されるエンドポイント
Heroku ルーターのログ形式
レガシールーターのログ
レガシールーターのログの形式は次のようになります。
情報ログ
次の内容は、ログドレインに送信される情報です。
264 <158>1 2012-10-11T03:47:20+00:00 host heroku router - at=info method=GET path=/ host=example-app-1234567890ab.herokuapp.com request_id=8601b555-6a83-4c12-8269-97c8e32cdb22 fwd="204.204.204.204" dyno=web.1 connect=1ms service=18ms status=200 bytes=13 tls_version=tls1.1 protocol=http
次の内容は、同じログ行を heroku logs
で表示した場合のものです。
2012-10-11T03:47:20+00:00 heroku[router]: at=info method=GET path=/ host=example-app-1234567890ab.herokuapp.com request_id=8601b555-6a83-4c12-8269-97c8e32cdb22 fwd="204.204.204.204" dyno=web.1 connect=1ms service=18ms status=200 bytes=13 tls_version=tls1.1 protocol=http
method
: HTTP リクエストのメソッドpath
: HTTP リクエストのパスとクエリ文字列host
: HTTP リクエストのHost
ヘッダーの値request_id
: Heroku の HTTP リクエスト IDfwd
: HTTP リクエストのX-Forwarded-For
ヘッダーの値dyno
: リクエストに対応した dyno の名前connect
: バックエンド Web プロセスへの接続の確立にかかった時間 (ミリ秒)service
: バックエンド Web プロセスとクライアントとの間のデータのプロキシにかかった時間 (ミリ秒)status
: HTTP 応答コードbytes
: バックエンド Web プロセスからクライアントに転送されたバイト数protocol
: リクエストのプロトコルを示しますtls_version
: 接続に使用された TLS バージョン。可能な値は ssl3.0、tls1.2、tls1.3、または unknown です。注意: これは Private Spaces の場合のみです。
エラーログ
次の内容は、ログドレインに送信される情報です。
277 <158>1 2012-10-11T03:47:20+00:00 host heroku router - at=error code=H12 desc="Request timeout" method=GET path=/ host=example-app-1234567890ab.herokuapp.com request_id=8601b555-6a83-4c12-8269-97c8e32cdb22 fwd="204.204.204.204" dyno=web.1 connect= service=30000ms status=503 bytes=0 protocol=http
次の内容は、同じログ行を heroku logs
で表示した場合のものです。
2012-10-11T03:47:20+00:00 heroku[router]: at=error code=H12 desc="Request timeout" method=GET path=/ host=example-app-1234567890ab.herokuapp.com request_id=8601b555-6a83-4c12-8269-97c8e32cdb22 fwd="204.204.204.204" dyno=web.1 connect= service=30000ms status=503 bytes=0 protocol=http
code
: Heroku のエラーコードdesc
: エラーの説明
Router 2.0 のログ
Common Runtime アプリで Router 2.0 を有効にすると、heroku[router]
ログは少し異なった形式になります。この新しい形式では、クライアントが使用する HTTP および TLS のバージョンに関する詳細情報が提供されます。Router 2.0 では以下の項目に違いがあります。
protocol
: HTTP バージョンを含むリクエストプロトコルを示します。値はhttp1.1
とhttp2.0
のいずれかです。tls
: 接続で TLS が使用されているかどうかを示します。値はtrue
とfalse
のいずれかです。tls_version
: 接続に使用された TLS バージョンを示します。値はtls1.2
、tls1.3
、unknown
のいずれかです。unknown
はデフォルトのherokuapp.com
ドメインにおけるすべての TLS 接続の値であることに注意してください。
以下は新しく改良された形式でのログ行の例です。
2024-04-26T16:58:32.943253+00:00 heroku[router]: at=info method=GET path="/" host=my-app.example.com request_id=6903a168-b79b-ec27-03c8-b8f64d8d8792 fwd=138.68.186.89 dyno=web.1 connect=0ms service=0ms status=200 bytes=0 protocol=http2.0 tls=true tls_version=tls1.3
キャッシング
大量の静的アセットに対応するアプリでは、HTTP キャッシングを利用すると、パフォーマンスを改善し、負荷を減らすことができます。
WebSocket
WebSocket 機能はすべてのアプリケーションでサポートされています。
gzip で圧縮された応答
Common Runtime のアプリへのリクエストは、nginx のような HTTP サーバーを介してプロキシされるのではなく、直接アプリケーションサーバーに送信されるため、応答の圧縮はアプリケーション内で行う必要があります。
サポートされている HTTP メソッド
Heroku の HTTP スタックは、RFC で定義されていないものも含め、CONNECT を除くあらゆる HTTP メソッド (「動詞 (verb)」とも呼ばれる) をサポートしています。
よく使われるメソッドは、GET、POST、PUT、DELETE、HEAD、OPTIONS、PATCH などです。メソッド名の長さは 127 文字に制限されています。
Expect: 100-continue
HTTP プロトコルには、サービス全体を向上させるためにクライアントがサーバーと連携できるようにする内蔵のメカニズムがいくつかあります。このようなメカニズムの 1 つに、リクエストと一緒に送信できる Expect: 100-continue
ヘッダーがあります [1]。
このヘッダーと値は、大量の HTTP リクエストを送信してもサーバーが安全に受け入れ可能かを事前に確認するために、信頼できるクライアントによって使用されます。これにより、サービス拒否の問題を防止し、一部の最適化を実現できます。cURL HTTP クライアントは、このメカニズムを使用する最もよく知られているライブラリで、content-body (コンテンツ本文) が 1 KB を超える場合にこの処理を行います (ドキュメントにない動作)。
サーバーは、許可を求められた場合はいつでも 100 Continue
HTTP ステータスで応答できます。これにより、サーバーがリクエストを受け入れられる状態であることをクライアントに通知して処理を継続させます。サーバーがリクエストを処理できない場合は、他の適切な HTTP 応答を返すことができ (負荷を処理できない場合は 413 Request Entity Too Large
など)、
その後、必要に応じてすぐに接続を終了できます。
そのため、2 つのクライアントとサーバーの間で行われるメカニズムは、通常のリクエストでは次のようになります。
[Client] [Server]
|-------- Partial Request -------->|
|<--------- 100 Continue ----------|
|--------- Request Body ---------->|
|<----------- Response ------------|
また、拒否されたリクエストの場合は、次のようになります。
[Client] [Server]
|-------- Partial Request -------->|
|<-- 413 Request Entity Too Large -|
ただし、すべてのサーバーとクライアントがそのメカニズムを認識できるとは限らないため、このメカニズムはより堅牢である必要があります。そのため、クライアントはサーバーが Expect: 100-Continue
ヘッダーを受け入れて適切に処理できるかどうか不明な場合、しばらく待った後で実際の本体を送信します。
[Client] [Server]
|-------- Partial Request -------->|
| |
| |
| |
|--------- Request Body ---------->|
|<----------- Response ------------|
デフォルトでは、多くの Web サーバーでは Expect: 100-continue
のメカニズムに対応していません。そのため、Heroku の HTTP ルーターは、ルーティング先のアプリケーションの代わりに自動的に 100 Continue
応答を挿入し、後でデータを転送します。
これにより、dyno の Web サーバーが関わっている限り、このメカニズムはほぼ全面的に無効になります。
100-Continue のサポートを有効にする
Heroku Labs 機能を通じてエンドツーエンドの継続サポートが利用できるようになりました。
$ heroku labs:enable http-end-to-end-continue
http-end-to-end-continue
の Heroku Labs 機能は、Router 2.0 を使用する場合、HTTP/1.1 トラフィックでのみサポートされます。
http-end-to-end-continue
は Fir 世代のアプリでは有効にできません。
この拡張機能が有効になっている場合は、100-Continue 機能の一般的なフローが復元され、ルーターによって expect: 100-continue
ヘッダーとそれらに関連する 100 continue
応答が透過的に渡されます。
ただし、この機能には、指定する必要がある独自のコーナーケースと動作があります。
コーナーケース
この種類のリクエストに付随する可能性がある一連のコーナーケースがあります。
元の HTTP 1.1 RFC (RFC 2068) では 100 Continue
部分応答を使用することで、サーバーに「リクエスト全体が解析されていませんが続けてください。すぐには拒否しません」と伝えさせることができました。その後の RFC (たとえば RFC 2616) では Expect: 100-continue
メカニズムが組み込まれ、100 Continue
の部分応答は前のセクションのテキストで定義されたメカニズムの一部として再定義されました。
後方互換性を維持するために、サーバーは関連する Expect
ヘッダーを受信していなくても 100 Continue
を送信できますが、そのようにしないことを強くお勧めします。
2 番目のコーナーケースとして、サーバーが直接本体データの受信と処理を開始した場合、100 Continue
応答を送信しない場合があります。クライアントはこれを認識します。
また、Expect
ヘッダーには 1 つ以上の値が含まれている可能性があるため、サーバーはその解析に注意する必要があります。たとえば、Expect: 100-continue, auth
というヘッダーは、サーバーが大きな本体を処理することを期待し、その前に認証を要求する場合に送信されます。技術的には、Expect
ヘッダーには任意の数の値を設定でき、その動作はクライアントとサーバーのためにのみ定義されます。
その他の特別なケースとして、接続フローを管理するヘッダーが複数あるために予期しないやり取りが発生する場合があります。たとえば、次のような場合があります。
- クライアントリクエストに、接続の Upgrade リクエスト (WebsSocket の場合) と
Expect: 100-continue
ヘッダーの両方が含まれている場合はどうなるか。 100 Continue
ステータスコードで応答するサーバーに、[Connection: close
(http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.10) のような受信時に接続を終了するヘッダーも含まれている場合はどうなるか。
これらの動作は元の仕様では未定義のため、動作の一貫性を保つためには、Heroku ルーターでこのような動作に関する判断を行う必要があります。
プロキシの要件
Expect
ヘッダーはエンドツーエンドのヘッダー (クライアントとサーバーのみがヘッダーを考慮する必要があるため、ヘッダー内で任意の用語を使用できる) として定義されていますが、100 Continue
メカニズム自体 (および Expect
の一般的な動作サポート) はプロキシとの調整を必要とし、ホップバイホップで実行されます。RFC では、次のような特別な条件が追加されています。
- プロキシはサーバーが処理できるかどうかに関係なく、ヘッダーをそのまま渡します。
- ルーティング先サーバーの HTTP バージョンが 1.0 以下であることが判明している場合、プロキシは
417 Expectation Failed
ステータスでリクエストを拒否する必要がありますが、判明していない可能性があります。 - HTTP 1.0 (またはそれ以前) のクライアントは
Expect: 100-Continue
ヘッダーなしでリクエストを送信できますが、それでもサーバーは (RFC 2068) の一環として)100 Continue
HTTP コードを使用してリクエストに応答できます。その場合、プロキシはその応答を完全に取り除き、最終ステータスの中継を待ちます。
Heroku Router 100-continue のサポート
Heroku ルーターは、未定義の動作や前述のコーナーケースに関して、一部、次のように独自の対応を行います。
100 Continue
は、クライアントが HTTP 1.0 (またはそれ以前) クライアントであり、リクエストにExpect: 100-continue
ヘッダーが含まれていない場合は取り除かれ、それ以外の場合はどんな状況でも転送されます。- ルーターはリクエスト本体の送信を開始するために
100 Continue
応答を要求しませんが、待ち時間はクライアントに委ねられます (Heroku の接続に関する通常の非アクティブルールに違反することはありません)。 Expect
ヘッダーに100 Continue
以外の値 (大文字と小文字を区別しない) が含まれている場合、ルーターは自動的に417 Expectation Failed
の応答を返し、dyno への接続を終了します。- WebSocket のアップグレードが要求された場合、そのリクエストはそのまま dyno に送信され、ルーターはどの応答が届いても対応します。
100 Continue
ステータスによって WebSocket のアップグレードが無視され、(通常どおりに) コードが返される可能性があります。また、101 Switching Protocol
によってExpect
ヘッダーの動作が無視されます。ただし、HTTP 仕様に従い、サーバーから100 Continue
を受信した後も、ルーターは101 Switching Protocol
を確認します。 - ルーターは
100 Continue
のConnection: close
を無視し、最終応答の受信後にのみ、この処理に対応します。RFC では「現在のリクエスト/応答が完了した後」に接続を終了するものと規定されており、100
はターミナル (終了) ステータスではないため、接続はターミナルステータスを受信するまで終了しません。ただし、Connection: close
はホップバイホップのメカニズムであるため、ルーターは必ずしもクライアントへの接続を終了せず、リクエストを転送しない可能性があります。 - RFC で規定されているヘッダーが存在しないため、ルーターは
100 Continue
応答からすべてのヘッダーを取り除きます。これにより、実装が大幅に簡素化されます。 - サーバーから初回の
100 Continue
応答の後で100 Continue
が返された場合、ルーターは 5xx エラーコードを返します。無限の 1xx ストリームは、まだサポートされていません。 - ルーターは、事前に
100 Continue
が返されたかどうかに関係なく、ターミナルステータスコードを返した後にサーバーへの接続を終了します。dyno への接続はキープアライブではありません。 - 事前に
100 Continue
が返されなかった場合、ルーターはターミナルステータスコードを返した後にクライアントへの接続を終了します。これにより、クライアントがリクエスト本体を送信しなくても、サーバーが次のリクエストを処理できるようになります。
その他のメカニズムにはプロトコルで現状どおりに従い、RFC の規定どおりにリクエストを転送します。
サポートされている HTTP のバージョン
HTTP の主要なバージョンは、HTTP/0.9、HTTP/1.0、HTTP/1.1、HTTP/2 の 4 つです。
レガシーの Heroku ルーターは、HTTP/1.0 と HTTP/1.1 のクライアントのみサポートしています。Router 2.0 は HTTP/2 をサポートしています。HTTP/0.9 以前のバージョンはサポートされていません。現在、SPDY はサポートされていません。Fir ルーターは、Router 2.0 と同じバージョンをサポートしており、HTTP/2 も含まれます。
HTTP/1.1 と HTTP/2 は、Web サーバーとブラウザ間におけるデータ通信を制御する Hypertext Transfer Protocol (ハイパーテキストトランスファープロトコル、HTTP) の 2 つのバージョンです。HTTP/2 は、多重化やヘッダー圧縮などの機能を導入してレイテンシーを削減することで、HTTP 1.1 よりも大幅に高速化されています。
ルーターの動作は、可能な限り HTTP/1.1 と HTTP/2 の仕様に従うものとします。ただし、HTTP/1.0 では、次のような特別な例外処理を行う必要があります。
- クライアントが HTTP/1.0 を使用しているかどうかにかかわらず、HTTP/1.1 を使用して Heroku ルーター自体をアドバタイズします。
- チャンク化された応答から通常の HTTP 応答への必要な変換を引き受けます。変換を行うときにギガバイト規模のデータが蓄積される可能性を防ぐため、クライアントへの応答は接続が終了した時点で区切られます (「Point 4.4.5)」を参照)
- クライアントがリクエストごとに接続を終了すること (キープアライブなし) を希望しているものと想定します。
- HTTP/1.0 クライアントは、明示的に
connection:keep-alive
ヘッダーを使用してリクエストを送信することがあります。1.0 ではキープアライブのメカニズムが定義されていなかった (アドホックだった) にもかかわらず、現時点で Heroku ルーターでは、リクエストされた動作が HTTP/1.1 の動作と類似しているものとみなします。
Router 2.0 と HTTP/2
Common Runtime アプリでは、Router 2.0 を有効にすると、HTTP/2 はデフォルトでオンになります。Common Runtime で HTTP/2 を使用する場合は、次の機能の考慮事項に注意してください。
- HTTP/2 は Heroku ルーターで終了し、ルーターからアプリには HTTP/1.1 を転送します。
- Common Runtime では、カスタムドメインで HTTP/2 がサポートされますが、組み込みの
<app-name-cff7f1443a49>.herokuapp.com
ドメインではサポートされません。 - HTTP/2 には有効な TLS 証明書が必要です。Heroku Automated Certificate Management の使用をお勧めします。
Heroku ルーターログの protocol
値を参照することで、アプリが HTTP/2 リクエストを受信していることを確認できます。
HTTP/2 をオプトアウトしても Router 2.0 を引き続き使用するには、アプリケーションで HTTP/2 を無効にします。Fir には機能フラグがないため、HTTP/2 は常に利用可能です。
HTTP の検証と制限
リクエストの検証:
- チャンク化されたエンコーディングと content-length の両方がリクエストで提示されている場合は、チャンク化されたエンコーディングが優先されます。
- 複数の content-length フィールドが提示されていて、それらが同じ長さである場合は、1 つの content-length ヘッダーにマージされます。
- content-length ヘッダーに複数の値 (
content-length: 15,24
) が含まれているか、複数の値を持つ複数の content-length ヘッダーがリクエストに含まれている場合、リクエストはコード 400 で拒否されます。 - ヘッダーは 1 行あたり 8192 バイト (ヘッダー名には 1000 バイト) に制限されています。
- 混乱を避けるために、ホップバイホップヘッダーは取り除かれます。
- 最大で 1 リクエストあたり 1000 個のヘッダーを使用できます。
- HTTP リクエストのリクエスト行は 8192 バイトに制限されています。
- リクエスト行では、単一のスペースで動詞 (verb)、パス、および HTTP バージョン間を区切る必要があります。
応答の検証:
- 混乱を避けるために、ホップバイホップヘッダーは取り除かれます。
- ヘッダーは 1 行あたり 512 KB に制限されています。
- Cookie は 明示的に 8192 バイトに制限されています。これは、まれに大きな Cookie 値を受け入れる一般的な制限 (たとえば、CDN によって課せられた制限など) から保護するためです。そのような場合、開発者が誤って大きな Cookie を設定してしまう可能性があり、それがユーザーに戻されると、ユーザーのすべてのリクエストが拒否される原因になります。
- ステータス行 (
HTTP/1.1 200 OK
) の長さは、8192 バイトに制限されています。
応答で上記の制限に違反しているアプリケーションのリクエストは、「502 Bad Gateway (不正なゲートウェイ)」の応答で失敗し、H25 エラーがアプリケーションのログストリームに送信されます。応答で上記の制限に違反しているクライアントのリクエストは、「400 Bad Request (不正なリクエスト)」の応答で失敗します。
さらに、HTTP/1.1 のリクエストと応答はデフォルトで keep-alive
であることが想定されていますが、初回のリクエストに明示的にルーターから dyno への connection: close
ヘッダーが含まれていた場合、dyno は特定の content-encoding や明示的な content-length を指定せず、接続が終了した時点で区切られた応答を送信できます。
プロトコルのアップグレード
以前の Heroku ルーターでは HTTP プロトコルのアップグレードが WebSocket のみに制限されていましたが、新しいルーターではすべてのアップグレードが許可されます。
実装に関する主なポイントは、以下のとおりです。
- アップグレード可能な接続では、あらゆる HTTP 動詞を使用できます。
- 通常、
HEAD
HTTP 動詞は回線経由での適切な応答の送信を要求しませんが (たとえば、content-length に関してなど)、HEAD
リクエストは明示的に101 Switching Protocols
応答と連携するようになります。アップグレードしない dyno は別のステータスコードを送信します。接続はアップグレードされません。
サポート対象外
- SPDY
- IPv6 は、Heroku の Cedar 世代ではサポートされていません。Fir 世代では IPv6 がサポートされています。
100-continue
以外のコンテンツを含んだExpect
ヘッダー (417 になります)- WEBDAV などの HTTP 拡張機能
- content-length またはチャンク化されたエンコーディングを含む HEAD、1xx、204、または 304 応答では、送信されることがない本体を中継するためのプロキシは試行されません
- CRLF (
\r\n
) 以外のヘッダー行末 - HTTP コンテンツのキャッシング
- dyno で実行している HTTP バージョンのサーバーのキャッシング
- 事前に割り当てられたアイドル接続の長時間待機。アイドル接続が終了するまでの制限は 1 分に設定されています。
Host
ヘッダーのない HTTP/1.0 リクエスト。リクエスト行で完全な URL が送信される場合もサポート対象外です。- TCP ルーティング
Common Runtime で使用可能な暗号スイート
デフォルトドメイン
すべてのデフォルトドメイン `(*.herokuapp.com)` トラフィック用にサポートされている暗号は、次のとおりです。 “` TLS_ECDHE-ECDSA-AES128-GCM-SHA256 TLS_ECDHE-RSA-AES128-GCM-SHA256 TLS_ECDHE-ECDSA-AES128-SHA256 TLS_ECDHE-RSA-AES128-SHA256 TLS_ECDHE-ECDSA-AES128-SHA TLS_ECDHE-RSA-AES128-SHA TLS_ECDHE-ECDSA-AES256-GCM-SHA384 TLS_ECDHE-RSA-AES256-GCM-SHA384 TLS_ECDHE-ECDSA-AES256-SHA384 TLS_ECDHE-RSA-AES256-SHA384 TLS_ECDHE-RSA-AES256-SHA TLS_ECDHE-ECDSA-AES256-SHA TLS_AES128-GCM-SHA256 TLS_AES128-SHA256 TLS_AES128-SHA TLS_AES256-GCM-SHA384 TLS_AES256-SHA256 TLS_AES256-SHA ”`カスタムドメイン
すべてのカスタムドメイントラフィック用にサポートされている暗号は、次のとおりです。 “` TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_RSA_WITH_AES_256_GCM_SHA384 TLS_RSA_WITH_AES_128_GCM_SHA256 ”`Private Spaces で利用可能な暗号スイート
リストについては、「Private Space でのルーティング」の「SSL セキュリティ」のセクションを参照してください。