031.3 レッスン 1
Certificate: |
Web開発の要点 |
---|---|
Version: |
1.0 |
Topic: |
031 ソフトウェア開発とWebテクノロジー |
Objective: |
031.3 HTTPの基本 |
Lesson: |
1 of 1 |
はじめに
HTTP HyperText Transfer Protocol は、クライアントがサーバに特定のリソースを要求する方法を定義しています。その動作原理は非常に単純です。クライアントは必要なリソースを特定したリクエストメッセージを作成し、そのメッセージをネットワーク経由でサーバに転送します。HTTPサーバは、リクエストされたリソースをどこで抽出するかを評価し、クライアントにレスポンスメッセージを送り返します。レスポンスメッセージには、要求されたリソースの詳細情報が含まれており、続いてリソースそのものが表示されます。
より具体的には、HTTPは、クライアントアプリケーションがサーバに送信する リクエスト メッセージをどのようにフォーマットするかを定義する一連のルールです。サーバは、HTTPルールに従ってリクエストを解釈し、 レスポンス メッセージをフォーマットします。HTTPメッセージには、リクエストされたコンテンツの要求や転送に加えて、関係するクライアントやサーバ、コンテンツ自体、さらにはその利用不可能性についての追加情報が含まれます。リソースを送信できない場合は、レスポンスに含まれるコードによって利用できない理由が説明され、可能であればリソースの移動先も示されます。
メッセージの中で、リソースの詳細情報やその他のコンテキスト情報を定義している部分をメッセージの ヘッダー と呼びます。ヘッダーに続く、対応するリソースのコンテンツを含む部分を、メッセージの ペイロード と呼びます。リクエストメッセージもレスポンスメッセージもペイロードを持つことができますが、ほとんどの場合、レスポンスメッセージのみがペイロードを持ちます。
クライアントのリクエスト
クライアントとサーバの間で行われるHTTPデータ交換の最初の段階は、クライアントがサーバにリクエストメッセージを書き込むことで始まります。例えば、ブラウザの一般的なタスクとして、 https://learning.lpi.org/ja/
といったWebサイトをホストしているサーバからHTMLページを読み込むことが挙げられます。このアドレスまたはURLには、いくつかの関連情報が含まれています。この例では、3つの情報が表示されています。
-
プロトコル : HTTPの暗号化バージョンであるHyperText Transfer Protocol Secure(
https
) -
Webホストのネットワーク名(
learning.lpi.org
) -
要求されたリソースのサーバ上の位置(
/ja/
ディレクトリ、この場合、ホームページの日本語版を指す。)
Note
|
Uniform Resource Locator (URL) とは、インターネット上のリソースを示すアドレスのことです。このリソースは通常、リモートサーバからコピーできるファイルですが、URLは動的に生成されるコンテンツやデータストリームを示すこともあります。 |
クライアントがURLを処理する方法
サーバに接続する前に、クライアントは、 learning.lpi.org
を対応するIPアドレスに変換する必要があります。クライアントは、もうひとつのインターネットサービスであるDNS (Domain Name System) を利用して、あらかじめ定義された1つまたは複数のDNSサーバにホスト名のIPアドレスを要求します(DNSサーバは通常、ISP(インターネットサービスプロバイダ)によって提供されます)。
サーバのIPアドレスを使って、クライアントはHTTPポートまたはHTTPSポートへの接続を試みます。ネットワークポートとは、クライアント/サーバ間の通信チャネルを相互に識別するために、 Transmission Control Protocol (TCP)によって定義された識別番号です。デフォルトでは、HTTPサーバはTCPポート80(HTTP)および443(HTTPS)でリクエストを受け取ります。
Note
|
Webアプリケーションでは、クライアント/サーバ間の通信を実現するために、他のプロトコルが使用されています。例えば、音声やビデオの通話には、データストリームを双方向に転送するためにHTTPよりも効率的な低レベルのプロトコルであるWebSocketを使用するのが適切です。 |
クライアントがサーバに送信するリクエストメッセージのフォーマットは、HTTPとHTTPSで同じです。HTTPSは、クライアントとサーバ間のすべてのデータ交換が暗号化されているため、すでにHTTPよりも広く使用されています。これは、公共のネットワークでプライバシーとセキュリティを促進するために不可欠な機能です。暗号化された接続は、HTTPメッセージが交換される前に、 Transport Layer Security (TLS)暗号プロトコルを使用して、クライアントとサーバの間で確立されます。これにより、すべてのHTTPS通信はTLSによってカプセル化されます。復号化された後は、HTTPSで送信されたリクエストやレスポンスは、HTTPのみで行われたリクエストやレスポンスと何ら変わりません。
URLの3番目の要素である /ja/
は、サーバによって、リクエストされたリソースの場所またはパスとして解釈されます。URLにパスが指定されていない場合は、デフォルトのロケーション /
が使用されます。HTTPサーバの最も単純な実装では、URL中のパスは、サーバが動作しているファイルシステム上のファイルと関連付けられますが、これは、より洗練されたHTTPサーバで利用可能な多くのオプションの一つに過ぎません。
リクエストメッセージ
HTTPは、クライアントとサーバの間ですでに確立された接続を介して動作します。通常、TCPで実装され、TLSで暗号化されています。実際には、サーバが要求する要件を満たす接続の準備ができていれば、平文で手書きのHTTPリクエストを入力しても、サーバからの応答を得ることができます。しかし、実際には、ほとんどのプログラミング言語がHTTPメッセージの作成を自動化する仕組みを備えているため、プログラマがHTTPメッセージを作成するルーチンを実装する必要はほとんどありません。例として挙げたURL、 https://learning.lpi.org/ja/
の場合、最もシンプルなリクエストメッセージは以下のような内容になります。
GET /ja/ HTTP/1.1 Host: learning.lpi.org User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0 Accept: text/html
1行目の最初の単語は、HTTP メソッド を識別します。これは、クライアントがサーバに対してどのような操作を行いたいかを定義します。GETメソッドは、クライアントがそれに続くリソース /ja/
を要求していることをサーバに知らせます。クライアントとサーバの両方がHTTPプロトコルの複数のバージョンをサポートしている可能性があるので、データ交換で採用されるバージョンも最初の行 HTTP/1.1
で提供されます。
Note
|
HTTPプロトコルの最新バージョンはHTTP/2です。他の違いとしては、HTTP/2で書かれたメッセージはバイナリ構造でエンコードされるのに対し、HTTP/1.1で書かれたメッセージはプレーンテキストで送信されます。この変更により、データ転送速度が最適化されましたが、メッセージの内容は基本的に同じです。 |
ヘッダーには、サーバへのリクエストを文脈に合わせて識別するために、最初の行の後にさらに多くの行を含めることができます。例えば、 Host
ヘッダーフィールドは冗長に見えるかもしれません。というのも、サーバのホストは、接続を確立するためにクライアントによって明らかに識別されており、サーバは自身のIDを知っていると考えるのが妥当だからです。しかし、同じHTTPサーバを使って複数のWebサイトをホストすることはよくあることなので、リクエストヘッダーで予想されるホスト名を通知することは重要です。(このシナリオでは、それぞれの特定のホストは、 バーチャルホスト と呼ばれます。)したがって、 Host
フィールドは、HTTPサーバが、リクエストがどのホストを参照しているかを識別するために使用されます。
User-Agent
ヘッダーフィールドは、リクエストを行うクライアントプログラムの詳細を含んでいます。このフィールドは、サーバが特定のクライアントのニーズに合わせてレスポンスを調整するために使用することができますが、サーバを使用しているクライアントの統計を取るために使用されることの方が多いです。
Accept
フィールドは、リクエストされたリソースのフォーマットをサーバに通知するので、より直接的な価値があります。クライアントがリソースのフォーマットに無頓着な場合は、 Accept
フィールドでのフォーマットとして */*
を指定することができます。
HTTPメッセージで使用できるヘッダーフィールドは他にもたくさんありますが、例で示したフィールドだけでサーバにリソースを要求することができます。
クライアントは、リクエストヘッダーのフィールドに加えて、サーバに送信するHTTPリクエストに他の補完的なデータを含めることができます。このデータが name=value
形式の単純なテキストパラメータだけで構成されている場合は、GETメソッドのパスに追加することができます。パラメータはクエスチョンマークの後のパスに埋め込まれ、アンパサンド( &
)文字で区切られます。
GET /cgi-bin/receive.cgi?name=LPI&email=info@lpi.org HTTP/1.1
この例では、 /cgi-bin/receive.cgi
がサーバ上のスクリプトへのパスであり、リクエストパスから得られたパラメータ name
と email
を処理し、場合によっては使用することになります。これらのフィールドに対応する name=LPI&email=info@lpi.org
形式の文字列は クエリ文字列 と呼ばれ、リクエストを受信したHTTP サーバから receive.cgi
スクリプトに提供されます。
データが短いテキストフィールド以上で構成されている場合は、メッセージのペイロードで送信するのが適切です。この場合、HTTP POSTメソッドを使用して、リクエストヘッダーに示された仕様に従って、サーバがメッセージのペイロードを受信して処理する必要があります。POSTメソッドを使用する場合、リクエストヘッダーには、次に送信されるペイロードのサイズと、ボディのフォーマットを指定する必要があります。
POST /cgi-bin/receive.cgi HTTP/1.1 Host: learning.lpi.org Content-Length: 1503 Content-Type: multipart/form-data; boundary=------------------------405f7edfd646a37d
Content-Length
フィールドはペイロードのバイトサイズを示し、 Content-Type
フィールドはそのフォーマットを示します。 multipart/form-data
フォーマットは、POSTメソッドを使用する伝統的なHTMLフォームで最もよく使用されるものです。このフォーマットでは、リクエストのペイロードに挿入される各フィールドは、 boundary
キーワードで示されるコードで区切られます。POSTメソッドは、GETメソッドによる同等のリクエストに比べて使用するデータ量が若干多いため、適切な場合にのみ使用する必要があります。GETメソッドでは、リクエストのメッセージヘッダーでパラメータを直接送信するため、メッセージボディを送信するための追加の接続ステージが必要ないため、データ交換全体の待ち時間が短くなります。
応答ヘッダー
HTTPサーバがリクエストメッセージヘッダーを受け取った後、クライアントにレスポンスメッセージを返します。HTMLファイルのリクエストは、通常、次のようなレスポンスヘッダーがあります。
HTTP/1.1 200 OK Accept-Ranges: bytes Content-Length: 18170 Content-Type: text/html Date: Mon, 05 Apr 2021 13:44:25 GMT Etag: "606adcd4-46fa" Last-Modified: Mon, 05 Apr 2021 09:48:04 GMT Server: nginx/1.17.10
最初の行には、応答メッセージで使用されているHTTPプロトコルのバージョンが示されています。このバージョンは、リクエストヘッダーで使用されているバージョンと一致していなければなりません。そして、やはり1行目には、サーバがリクエストに対してどのように解釈してレスポンスを生成したかを示す、レスポンスのステータスコードが表示されます。
ステータスコードは3桁の数字で、左端の桁がレスポンスクラスを表します。ステータスコードには、1から5までの5つのクラスがあり、それぞれサーバが取るアクションの種類を示しています。
- 1xx (情報提供)
-
リクエストを受信し、処理を継続します。
- 2xx (成功)
-
リクエストは正常に受信され、理解され、受け入れられました。
- 3xx (リダイレクト)
-
リクエストを完了させるために、さらにアクションを起こす必要があります。
- 4xx (クライアントエラー)
-
リクエストに不正な構文が含まれているか、実行できません。
- 5xx (サーバエラー)
-
サーバは、明らかに有効な要求の実行に失敗しました。
2桁目と3桁目は追加の詳細を示すために使用されます。例えば、コード200は、リクエストに問題なく回答できたことを示します。例に示すように、応答コード( OK
)に続く簡単なテキストの説明を提供することもできます。いくつかの特定のコードは、不利な状況でもHTTPクライアントがリソースにアクセスできることを保証したり、リクエストが失敗した場合に失敗の理由を特定するのに役立つ、特に重要なものです。
301 Moved Permanently
-
対象となるリソースには、レスポンスの
Location
ヘッダーフィールドで提供される新しいパーマネントURLが割り当てられました。 302 Found
-
対象のリソースが一時的に別のURLで存在しています。
401 Unauthorized
-
対象のリソースに対する有効な認証情報がないため、リクエストは適用されませんでした。
403 Forbidden
-
Forbidden
レスポンスは、リクエストは有効だが、サーバがリクエストを提供しないように設定されていることを示します。 404 Not Found
-
オリジンサーバが、対象となるリソースの現在の表現を見つけられなかったか、あるいは存在することを公表する意思がないことを示しています。
500 Internal Server Error
-
サーバが予期せぬ状況に陥り、リクエストを遂行できなくなりました。
502 Bad Gateway
-
サーバがゲートウェイやプロキシとして動作している間に、リクエストを実行しようとしてアクセスした内部のサーバから無効なレスポンスを受信しました。
ステータスコード 4xx
と 5xx
は、リクエストに応えることができなかったことを示していますが、少なくとも HTTP サーバが稼働しており、リクエストを受信することができることを示しています。 4xx
のコードは、クライアントのURLや認証情報が間違っているため、クライアントでアクションを起こすことが必要です。これに対して、 5xx
コードは、サーバ側に何か問題があることを示しています。したがって、Webアプリケーションのコンテキストでは、これらの2つのクラスのステータスコードは、エラーの原因が基礎となるインフラではなく、クライアントまたはサーバのいずれかのアプリケーション自体にあることを示します。
静的および動的コンテンツ
HTTPサーバは、2つの基本的なメカニズムを使って、クライアントから要求されたコンテンツを実現します。1つ目のメカニズムは、 静的コンテンツ を提供します。つまり、リクエストメッセージで示されたパスは、サーバのローカルファイルシステム上のファイルに対応します。2つ目のメカニズムは、 動的コンテンツ を提供します。つまり、HTTPサーバはリクエストを別のプログラム(おそらくスクリプト)に転送し、データベースや他のファイルなどの異なるソースからレスポンスを構築します。
HTTPサーバにはさまざまな種類がありますが、いずれも同じHTTP通信プロトコルを使用し、多かれ少なかれ同じ規約を採用しています。特定のニーズがないアプリケーションであれば、ApacheやNGINXなど、従来のサーバでも実装可能です。どちらも動的コンテンツの生成と静的コンテンツの提供が可能ですが、それぞれの設定には微妙な違いがあります。
例えば、提供される静的ファイルの場所は、ApacheとNGINXで異なる方法で定義されています。規約では、この目的のために特定のディレクトリにこれらのファイルを保持することになっており、そのディレクトリにはホストに関連した名前、例えば /var/www/learning.lpi.org/
が付けられます。Apacheでは、このパスは、バーチャルホストを定義するセクションの中で、設定ディレクティブ DocumentRoot /var/www/learning.lpi.org
によって定義されます。NGINXでは、設定ファイルの server
セクションの root /var/www/learning.lpi.org
が使われています。
どちらのサーバを選択しても、 /var/www/learning.lpi.org/
にあるファイルは、ほぼ同じ方法で HTTP 経由で提供されます。レスポンスヘッダーのいくつかのフィールドとその内容は2つのサーバ間で異なるかもしれませんが、 Content-Type
のようなフィールドはレスポンスヘッダーに存在しなければならず、どのサーバ間でも一貫していなければなりません。
キャッシング
HTTPは、高速・低速を問わず、あらゆるタイプのインターネット接続で動作するように設計されています。さらに、インターネットは分散型のアーキテクチャを採用しているため、ほとんどのHTTP通信は多くのネットワークノードを通過しなければなりません。そのため、以前にダウンロードしたコンテンツの冗長な転送を避けるために、何らかのコンテンツキャッシング戦略を採用することが重要です。HTTP転送は、2つの基本的なタイプのキャッシュで動作します。 共有 と プライベート です。
共有キャッシュは、複数のクライアントによって使用されます。例えば、大規模なコンテンツプロバイダーでは、地理的に分散したサーバでキャッシュを使用し、クライアントが最寄りのサーバからデータを取得できるようにしています。あるクライアントがリクエストを行い、そのレスポンスが共有キャッシュに保存されると、同じ地域で同じリクエストを行った他のクライアントは、キャッシュされたレスポンスを受け取ることになります。
プライベートキャッシュとは、クライアント自身が自分専用に作成するものです。Webブラウザが画像、CSSファイル、JavaScript、HTMLドキュメントなどをキャッシュすることで、近い将来にリクエストされても、再度ダウンロードする必要がないようにするものです。
Note
|
すべてのHTTPリクエストをキャッシュしなければならないわけではありません。例えば、POSTメソッドを使用したリクエストは、その特定のリクエストにのみに関連するレスポンスを意味するため、そのレスポンスの内容を再利用してはいけません。デフォルトでは、GETメソッドを使用したリクエストに対するレスポンスのみがキャッシュされます。さらに、200 (OK)、206 (Partial Content)、301 (Moved Permanently)、404 (Not Found)などの決定的なステータスコードを持つレスポンスのみがキャッシュに適しています。 |
共有キャッシュ、プライベートキャッシュともに、ダウンロードしたコンテンツをどのようにキャッシュするかを制御するために、HTTPヘッダーを使用します。プライベートキャッシュの場合、クライアントはレスポンスヘッダーを参照し、ローカルキャッシュ内のコンテンツが現在のリモートコンテンツにまだ対応しているかどうかを検証します。対応している場合、クライアントは応答ペイロードの転送を放棄し、ローカルバージョンを使用します。
キャッシュされたリソースの有効性は、いくつかの方法で評価することができます。サーバは最初のリクエストの応答ヘッダーに有効期限を指定することで、クライアントは期間終了時にキャッシュリソースを破棄し、更新されたバージョンを取得するために再度リクエストすることができます。しかし、サーバはリソースの有効期限を常に判断できるわけではないので、リソースのバージョンを識別するために、 例えば、 Etag: "606adcd4-46fa"
のように、 ETag
レスポンスヘッダーフィールドを使用するのが一般的です。
キャッシュされたリソースに更新の必要性があるかどうかを確認するために、クライアントはサーバからの応答ヘッダーのみを要求します。 ETag
フィールドがローカルに保存されているバージョンのものと一致すれば、クライアントはキャッシュされたコンテンツを再利用します。それ以外の場合は、リソースの更新されたコンテンツをサーバからダウンロードします。
HTTPセッション
従来のWebサイトやWebアプリケーションでは、セッション制御を処理する機能は、HTTPヘッダーに基づいています。例えば、サーバは、同じIPアドレスから来るすべてのリクエストが同じクライアントからのものであると仮定することはできません。サーバが異なるリクエストを1つのクライアントに関連付けることができる最も伝統的な方法は、 cookies を使用することです。クッキーとは、サーバからクライアントに与えられる識別タグで、HTTPヘッダーに記載されています。
クッキーを使用すると、クライアントを実行している人が自分自身を明確に識別していなくても、サーバは特定のクライアントに関する情報を保持することができます。クッキーを使用すると、ログイン、ショッピングカート、環境設定などを、それらを提供した同一のサーバへの異なるリクエストの間で保存するセッションを実現することができます。クッキーはユーザーのブラウジングを追跡するためにも使用されるので、送信する前に同意を得ることが重要です。
サーバはレスポンスヘッダーの中で、 Set-Cookie
フィールドを使ってクッキーを設定します。フィールドの値は、特定のクライアントに関連する何らかの属性を表すように選択された name=value
のペアです。サーバは例えば、あるリソースを初めて要求したクライアントのために識別番号を作成し、それをレスポンスヘッダーの中でクライアントに伝えることができます。
HTTP/1.1 200 OK Accept-Ranges: bytes Set-Cookie: client_id=62b5b719-fcbf
クライアントがクッキーの使用を許可している場合、同じサーバへの新しいリクエストには、ヘッダーにクッキーフィールドが含まれます。
GET /ja/ HTTP/1.1 Host: learning.lpi.org Cookie: client_id=62b5b719-fcbf
この識別番号を使って、サーバはクライアントの特定の定義を取得し、カスタマイズされた応答を生成することができます。また、複数の Set-Cookie
フィールドを使用して、同じ顧客に異なるクッキーを提供することも可能です。このようにして、クライアント側で複数の定義を保存することができます。
クッキーは、サーバによって元のクライアントとして識別される別のクライアントに転送される可能性があるため、プライバシーの問題と潜在的なセキュリティホールの可能性の両方をもたらします。セッションを保持するために使用されたクッキーは、元のクライアントの機密情報にアクセスできる可能性があります。したがって、クライアントは、クッキーが許可なく抽出されたり再利用されたりしないように、ローカルな保護メカニズムを採用することが非常に重要です。
演習
-
次のリクエストメッセージが使用するHTTPメソッドは何ですか?
POST /cgi-bin/receive.cgi HTTP/1.1 Host: learning.lpi.org User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0 Accept: */* Content-Length: 27 Content-Type: application/x-www-form-urlencoded
-
HTTPサーバが多くのWebサイトをホストしている場合、どのようにしてリクエストを識別することができるのでしょうか?
-
URL
https://www.google.com/search?q=LPI
のクエリストリングで提供されるパラメータは何ですか? -
次のHTTPリクエストがキャッシングに適していないのはなぜですか?
POST /cgi-bin/receive.cgi HTTP/1.1 Host: learning.lpi.org User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0 Accept: */* Content-Length: 27 Content-Type: application/x-www-form-urlencoded
発展演習
-
Webブラウザを使って、HTMLページのリクエストとレスポンスをどのように監視することができますか?
-
静的コンテンツを提供するHTTPサーバは、通常、リクエストされたパスをサーバのファイルシステム内のファイルにマッピングします。では、リクエストされたパスがディレクトリを指していた場合はどうなるでしょうか。
-
HTTPS で送信されるファイルの内容は暗号化によって保護されているため、クライアントとサーバの間にあるコンピュータが読むことはできません。にもかかわらず、中間のコンピュータは、クライアントがサーバに要求したリソースを特定することはできるでしょうか?
まとめ
このレッスンでは、クライアントアプリケーションがWebサーバにリソースを要求する際に使用する主要なプロトコルである、HTTPの基本について説明しました。このレッスンでは、次のような概念について説明しました。
-
リクエストメッセージ、ヘッダーフィールド、およびメソッド
-
レスポンスのステータスコード
-
HTTPサーバがレスポンスを生成する方法
-
キャッシングやセッション管理に役立つHTTPの機能
演習の回答
-
次のリクエストメッセージが使用するHTTPメソッドは何ですか?
POST /cgi-bin/receive.cgi HTTP/1.1 Host: learning.lpi.org User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0 Accept: */* Content-Length: 27 Content-Type: application/x-www-form-urlencoded
POSTメソッド
-
HTTPサーバが多くのWebサイトをホストしている場合、どのようにしてリクエストを識別することができるのでしょうか?
リクエストヘッダーの
Host
フィールドは、対象となるWebサイトを示しています。 -
URL
https://www.google.com/search?q=LPI
のクエリストリングで提供されるパラメータは何ですか?値が
LPI
のq
という名前のパラメータです。 -
次のHTTPリクエストがキャッシングに適していないのはなぜですか?
POST /cgi-bin/receive.cgi HTTP/1.1 Host: learning.lpi.org User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0 Accept: */* Content-Length: 27 Content-Type: application/x-www-form-urlencoded
POSTメソッドによるリクエストは、サーバへの書き込み操作を意味するため、キャッシュされるべきではありません。
発展演習の回答
-
Webブラウザを使って、HTMLページのリクエストとレスポンスをどのように監視することができますか?
一般的なブラウザには 開発ツール が用意されており、現在のページで実行されたすべてのネットワークトランザクションを表示することなどが可能です。
-
静的コンテンツを提供するHTTPサーバは、通常、リクエストされたパスをサーバのファイルシステム内のファイルにマッピングします。では、リクエストされたパスがディレクトリを指していた場合はどうなるでしょうか。
それは、サーバの設定方法によります。デフォルトでは、ほとんどのHTTPサーバは、同じディレクトリにある
index.html
という名前のファイル(またはあらかじめ定義された別の名前)を探して、それをレスポンスとして送信します。ファイルが存在しない場合、サーバは404 Not Found
応答を発行します。 -
HTTPS で送信されるファイルの内容は暗号化によって保護されているため、クライアントとサーバの間にあるコンピュータが読むことはできません。にもかかわらず、中間のコンピュータは、クライアントがサーバに要求したリソースを特定することはできるでしょうか?
いいえ、リクエストとレスポンスのHTTPヘッダー自体もTLSで暗号化されているからです。