Zenn
🌐

Azure Application GatewayでHTTP/2を試してみた

2024/12/22に公開

はじめに

Application GatewayでHTTP/2を試してみました。
Application GatewayでHTTP/2を使う際のHTTPS接続とHTTP接続との振る舞いやログ出力結果など、試した内容をまとめています。

HTTP/2とは

2015年に標準化された比較的新しいHTTP通信を行うためのプロトコルです。
従来のHTTP/1.1に比べ効率がよく高速な通信が行えます。

HTTP/2では1つのTCPコネクション上で同時に複数のリクエストを処理したり、ヘッダーの圧縮にも対応しており、コンテンツ数が多いWEBサイトなどで特に高速化が期待できます。

HTTP/2はHTTP接続とHTTPS接続の両方ともサポートはしているものの、実態として主要なブラウザはHTTPS接続でのHTTP/2のみをサポートしています。

Application GatewayでHTTP/2を使う

PoC構成

Client VMからPrivate Application Gatewayにアクセスし、バックエンドのServer VMで稼働しているNGINXのWEBページを取得します。
HTTPSリスナーには、Learnの手順で作成した自己署名証明書(Common Name:www.fabrikam.com)を使用します。

環境を作成するためのTerraformコードはこちらのリポジトリに置いてあります。

HTTP/1.1で通信してみる

Application GatewayデフォルトのHTTP/2を無効にした状態で、HTTP/1.1で接続してみます。

HTTP

まずはHTTP接続をしてみます。

curl結果で確認したい行だけ抜粋
$ curl -v -k http://www.fabrikam.com/ --resolve www.fabrikam.com:80:192.168.101.20

> GET / HTTP/1.1
< HTTP/1.1 200 OK
curl 結果全文
$ curl -v -k http://www.fabrikam.com/ --resolve www.fabrikam.com:80:192.168.101.20
* Added www.fabrikam.com:80:192.168.101.20 to DNS cache
* Hostname www.fabrikam.com was found in DNS cache
*   Trying 192.168.101.20:80...
* Connected to www.fabrikam.com (192.168.101.20) port 80
> GET / HTTP/1.1
> Host: www.fabrikam.com
> User-Agent: curl/8.5.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Sun, 22 Dec 2024 05:02:10 GMT
< Content-Type: text/html
< Content-Length: 28
< Connection: keep-alive
< Server: nginx/1.24.0 (Ubuntu)
< Last-Modified: Sun, 22 Dec 2024 04:55:55 GMT
< ETag: "67679bdb-1c"
< Accept-Ranges: bytes
< 
azpoc-agw-usw-vm-server-001
* Connection #0 to host www.fabrikam.com left intact

HTTP接続が成功し、プロトコルはHTTP/1.1が使用されました。

HTTPS

次はHTTPS接続をしてみます。

curl結果で確認したい行だけ抜粋
$ curl -v -k https://www.fabrikam.com/ --resolve www.fabrikam.com:443:192.168.101.20
* ALPN: curl offers h2,http/1.1
* ALPN: server accepted http/1.1
> GET / HTTP/1.1
< HTTP/1.1 200 OK
curl 結果全文
$ curl -v -k https://www.fabrikam.com/ --resolve www.fabrikam.com:443:192.168.101.20
* Added www.fabrikam.com:443:192.168.101.20 to DNS cache
* Hostname www.fabrikam.com was found in DNS cache
*   Trying 192.168.101.20:443...
* Connected to www.fabrikam.com (192.168.101.20) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / id-ecPublicKey
* ALPN: server accepted http/1.1
* Server certificate:
*  subject: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=www.fabrikam.com
*  start date: Nov 19 13:17:30 2024 GMT
*  expire date: Nov 19 13:17:30 2025 GMT
*  issuer: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=www.contoso.com
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
*   Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA256
* using HTTP/1.x
> GET / HTTP/1.1
> Host: www.fabrikam.com
> User-Agent: curl/8.5.0
> Accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/1.1 200 OK
< Date: Sun, 22 Dec 2024 05:02:27 GMT
< Content-Type: text/html
< Content-Length: 28
< Connection: keep-alive
< Server: nginx/1.24.0 (Ubuntu)
< Last-Modified: Sun, 22 Dec 2024 04:55:59 GMT
< ETag: "67679bdf-1c"
< Accept-Ranges: bytes
< 
azpoc-agw-usw-vm-server-002
* Connection #0 to host www.fabrikam.com left intact

HTTPS接続が成功し、プロトコルはHTTP/1.1が使用されました。

HTTPS接続時には、ALPN(Application-Layer Protocol Negotiation)を用いてTLSハンドシェイク時にクライアントが対応しているプロトコルをサーバーに送信し、使用するプロトコルをサーバーが選択します。

  • ALPN: curl offers h2,http/1.1

クライアントのcurlはh2(HTTP/2 over TLS)とHTTP/1.1を対応プロトコルとして送信し、

  • ALPN: server accepted http/1.1

Application GatewayはHTTP/1.1を選択したことがわかります。

HTTP/2で通信してみる

Application GatewayでHTTP/2を有効に設定し、HTTP/2で通信してみます。

HTTP

curlの--http2オプションでプロトコルをHTTP/2で指定して、HTTP接続をしてみます。

curl結果で確認したい行だけ抜粋
$ curl --http2 -v -k http://www.fabrikam.com/ --resolve www.fabrikam.com:80:192.168.101.20
> GET / HTTP/1.1
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAAQAoAAAAAIAAAAA
> 
< HTTP/1.1 403 Forbidden
curl 結果全文
$ curl --http2 -v -k http://www.fabrikam.com/ --resolve www.fabrikam.com:80:192.168.101.20
* Added www.fabrikam.com:80:192.168.101.20 to DNS cache
* Hostname www.fabrikam.com was found in DNS cache
*   Trying 192.168.101.20:80...
* Connected to www.fabrikam.com (192.168.101.20) port 80
> GET / HTTP/1.1
> Host: www.fabrikam.com
> User-Agent: curl/8.5.0
> Accept: */*
> Connection: Upgrade, HTTP2-Settings
> Upgrade: h2c
> HTTP2-Settings: AAMAAABkAAQAoAAAAAIAAAAA
> 
< HTTP/1.1 403 Forbidden
< Server: Microsoft-Azure-Application-Gateway/v2
< Date: Sun, 22 Dec 2024 05:03:29 GMT
< Content-Type: text/html
< Content-Length: 179
< Connection: keep-alive
< 
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>Microsoft-Azure-Application-Gateway/v2</center>
</body>
</html>
* Connection #0 to host www.fabrikam.com left intact

403 Forbiddenになりました。

Connection: Upgrade, HTTP2-Settings
Upgrade: h2c

curlで--http2オプションを指定したので、Upgrade: h2cヘッダでh2c(HTTP/2 over cleartext TCP)へのアップグレードが試みられたのですが、

HTTP/1.1 403 Forbidden

HTTPレスポンスのプロトコルはHTTP/1.1が使用されています。
HTTPレスポンスコードも403 Forbiddenになってしまいました。

診断ログを見ると、Application Gatewayの段階でブロックされているわけではなく、バックエンドサーバーへの通信は行われていることが分かります。

バックエンドプールに設定したServer VMのNGINXのアクセスログでも、Application Gatewayからのリクエストに対して、HTTPレスポンスコード200を返却していることが分かります。

$ tail -f /var/log/nginx/access.log
192.168.101.23 - - [22/Dec/2024:05:03:29 +0000] "GET / HTTP/1.1" 200 28 "-" "curl/8.5.0"

curlはHTTP接続でHTTP/2へのUpgradeヘッダでアップグレード要求を送っているので、Application GatewayがHTTP/2に未対応であれば、アップグレードしないでHTTP/1.1のままバックエンドプール(Server VM)のレスポンスを返してほしい気もしますが、Application GatewayはHTTPレスポンスコード403で応答します。

診断ログにはHTTP2へのアップグレードが失敗したことを示す文言は出力されないので、Application GatewayがHTTPレスポンスコードを403に書き換えて応答している理由が読み取りにくいかもしれません。

現時点では、HTTP/2が有効化されたApplication Gatewayは、HTTP接続時にUpgradeヘッダが付与されているとHTTPレスポンスコードを403を返すので、Application GatewayでHTTP/2を使用する場合はリスナーをHTTPSに設定する必要があるようです。

HTTPS

次はHTTPS接続をしてみます。

curl結果で確認したい行だけ抜粋
$ curl -v -k https://www.fabrikam.com/ --resolve www.fabrikam.com:443:192.168.101.20
* ALPN: curl offers h2,http/1.1
* ALPN: server accepted h2
* using HTTP/2
> GET / HTTP/2
< HTTP/2 200 
curl 結果全文
$ curl -v -k https://www.fabrikam.com/ --resolve www.fabrikam.com:443:192.168.101.20
* Added www.fabrikam.com:443:192.168.101.20 to DNS cache
* Hostname www.fabrikam.com was found in DNS cache
*   Trying 192.168.101.20:443...
* Connected to www.fabrikam.com (192.168.101.20) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / id-ecPublicKey
* ALPN: server accepted h2
* Server certificate:
*  subject: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=www.fabrikam.com
*  start date: Nov 19 13:17:30 2024 GMT
*  expire date: Nov 19 13:17:30 2025 GMT
*  issuer: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=www.contoso.com
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
*   Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA256
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://www.fabrikam.com/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: www.fabrikam.com]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.5.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: www.fabrikam.com
> User-Agent: curl/8.5.0
> Accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/2 200 
< date: Sun, 22 Dec 2024 05:10:18 GMT
< content-type: text/html
< content-length: 28
< server: nginx/1.24.0 (Ubuntu)
< last-modified: Sun, 22 Dec 2024 04:55:59 GMT
< etag: "67679bdf-1c"
< accept-ranges: bytes
< 
azpoc-agw-usw-vm-server-002
* Connection #0 to host www.fabrikam.com left intact

HTTPS接続が成功し、プロトコルはHTTP/2が使用されました。

  • ALPN: curl offers h2,http/1.1

クライアントのcurlはh2(HTTP/2 over TLS)とHTTP/1.1を対応プロトコルとして送信し

  • ALPN: server accepted h2

今回はApplication Gatewayはh2(HTTP/2 over TLS)を選択したことがわかります。

最後に

このような結果になりました。

HTTP/1.1 HTTP/2
HTTP接続 ×
HTTPS接続

Application GatewayはHTTP接続でのHTTP/2はサポートしておらず、HTTP接続でのHTTP/2リクエストを自動的にHTTP/1.1にダウングレードもしないようです。
今回はcurlを使いましたが、そもそも主要なブラウザはHTTPS接続でのHTTP/2しかサポートしていないので、HTTP接続はHTTP/1.1で行われます。(HTTP/2へのUpgradeヘッダを付与しない)
そのため、ブラウザからのHTTP接続あればHTTPレスポンスコード403にはなりません。

注意が必要な点として、一部のライブラリではHTTP/2がデフォルトになっておりUpgrade: h2cヘッダを付与するものがあります。
このような場合、Application GatewayではUpgrade: h2cヘッダが付与されたHTTPリクエストは403になってしまうので、明示的にHTTP/1.1として接続するか、HTTPSで接続する必要がありそうです。

Application GatewayではHTTP3もPreview入りしているようなので、こちらも試してみたいです!

参考

Discussion

ログインするとコメントできます