IIS 10のHTTP/2でjQuery AJAX(ASMX POST)がnet::ERR_HTTP2_PROTOCOL_ERRORになる原因と対処法

Windows Server 2016(IIS 10)でHTTP/2を有効にしたところ、ASMX WebサービスへjQuery(XHR/AJAX)POSTがnet::ERR_HTTP2_PROTOCOL_ERRORで失敗する――本番は正常なのに検証環境だけ再現するケースで、原因の当たり所と、現場で困らない切り分け・回避策をまとめます。

目次

症状:IIS 10(HTTP/2)環境で、ASMXへのjQuery POSTだけが失敗する

現象としては次のような形で表面化します。

  • Windows Server 2016(IIS 10)で稼働する .NET Framework 4 系のWebアプリ
  • HTTPS(TLS 1.2)かつHTTP/2(h2)で配信
  • 同一ドメイン内のASMX Webサービスに対して、jQueryの$.ajax(XMLHttpRequest)でPOST
  • Chromeのコンソールで net::ERR_HTTP2_PROTOCOL_ERROR が出て失敗
  • HTTP/2を無効化してHTTP/1.1にすると正常化
  • Wireshark上はHTTP/2のRST_STREAMが見える
POST https://example.com/asmxservice.asmx/getUserLogged net::ERR_HTTP2_PROTOCOL_ERROR

この手の現象は「jQueryが悪い」「ASMXが悪い」というより、HTTP/2のセッション(ストリーム)を途中でサーバー側(または途中の機器)がリセットしている結果として、ブラウザが“プロトコルエラー”扱いにしているケースが多いです。

まず押さえる:net::ERR_HTTP2_PROTOCOL_ERRORは「HTTP/2の会話が壊れた」合図

HTTP/2では、1本のTCP/TLS接続の中に「ストリーム」という単位で複数のリクエスト/レスポンスを多重化します。ブラウザがnet::ERR_HTTP2_PROTOCOL_ERRORを出すとき、典型は次のどれかです。

  • サーバー(または中継)がRST_STREAM(ストリーム中断)やGOAWAY(接続終了)を返している
  • HTTP/2として不正なヘッダー/フレーム(=“壊れたメッセージ”)が混ざった
  • プロキシ/ロードバランサがHTTP/1.1⇄HTTP/2変換の過程で、禁止ヘッダーを残してしまった

特に重要なのが「HTTP/2ではHTTP/1.1の“接続固有ヘッダー”が禁止」というルールです。例えばConnection / Keep-Alive / Transfer-Encoding / Upgrade / Proxy-ConnectionがHTTP/2メッセージに含まれると“壊れたメッセージ(malformed)”として扱われ、プロトコルエラーになり得ます。

また、HTTP/2では本文(ボディ)はDATAフレームで運ばれるため、HTTP/1.1で使うTransfer-Encoding: chunkedは使えません。もし中継の変換ミス等でこれが残ると、ブラウザ側は正しく処理できず、結果としてERR_HTTP2_PROTOCOL_ERRORのような形で落ちます。

さらに、HTTP/2ではヘッダー名は小文字であることが求められ、ヘッダー名に大文字が混ざるだけでも“malformed”扱いになります(HTTP/1.1では問題にならないのにHTTP/2だと致命傷、という典型例です)。

「Max Concurrent Streams = 100」が見えるときの考え方

Wireshark等でMax Concurrent Streams = 100(正確にはHTTP/2のSETTINGSパラメータSETTINGS_MAX_CONCURRENT_STREAMS)が見えると、「100が低いから増やせば解決?」と考えたくなります。

ただしHTTP/2の仕様上、SETTINGS_MAX_CONCURRENT_STREAMSは“相手に同時に作ってよいストリーム数の上限”を伝える設定で、初期値は「無制限」扱いです。加えて、値は「100より小さくしないことが推奨」とされており、100自体は“変な値”ではありません。

つまり、100が見えた=それだけで異常とは言い切れません。むしろ、次のような条件が重なると「100が見えているだけなのにRSTが出る」状況が作られます。

  • 検証環境だけ、LB/リバースプロキシ/WAF/SSL検査が入っていて、HTTP/2⇄HTTP/1.1変換の品質が違う
  • 検証環境だけ、OS/IIS/HTTP.sysの更新状態が異なりHTTP/2の挙動差(バグ修正/回帰)がある
  • 検証環境だけ、HTTP/2関連のレジストリ制限(セキュリティ対策)が強く入っている
  • ページ側で同一オリジンに対する同時リクエストが多く、偶然“限界近辺”を踏む(特にHTTP/2は1接続に集約されやすい)

このQ&Aが示唆する結論:アプリではなくIIS/HTTP/2層の問題として扱う

元となったQ&Aでは、採用された回答が「IIS(HTTP/2)側の問題の可能性が高いのでIISフォーラムへ」という誘導で、最大同時ストリーム数の調整や根本原因の提示はありませんでした。

これは裏を返すと、“jQueryの書き方で直るタイプ”ではない可能性が高い、という示唆でもあります。実務では、まず「HTTP/2を切ると直る」という事実を足掛かりに、環境差分とHTTP/2通信の健全性を潰すのが最短です。

切り分けの最短ルート:まず「本当にHTTP/2で失敗しているか」を確定する

“HTTP/2が関係している”を推測で終わらせず、ログ・ツールで確定させます。

ブラウザでプロトコルを可視化する

  • Chrome/EdgeのDevTools → Networkで「Protocol」列(h2 / http/1.1)を表示して、失敗するリクエストがh2になっているかを見る
  • 同じURLを別ブラウザでも試し、再現性を確認する(クライアント実装差の影響を除外する)

IISログに「プロトコル版」を出す

IISログには“Protocol Version(cs-version)”を出せます。HTTP/2とHTTP/1.1が混在する環境では、これを出しておくと「どのリクエストがHTTP/2だったのか」を後追いできます。IISのHTTP/2はアプリ側で意識せずに“透過的に”動く前提で設計されているため、ログで握るのが重要です。

curlでHTTP/2とHTTP/1.1を同条件で比較する

可能なら、同じクライアントからHTTP/2とHTTP/1.1を強制して比較します。POSTの場合はヘッダーと本文も揃えます。

# HTTP/2で実行(verboseでプロトコルとヘッダーを確認)
curl -vk --http2 https://example.com/asmxservice.asmx/getUserLogged -X POST -d "a=1&b=2"

# HTTP/1.1で実行(HTTP/2切り替え差を確認)

curl -vk --http1.1 [https://example.com/asmxservice.asmx/getUserLogged](https://example.com/asmxservice.asmx/getUserLogged) -X POST -d "a=1&b=2"

「本番はOK、検証だけNG」を潰すための差分チェック表

HTTP/2は“途中のどこか”が壊れると一気に表面化します。以下の差分を機械的に潰すと、遠回りに見えて最短になります。

差分ポイントなぜ効くか確認方法(例)よくある落とし穴
Windowsのビルド/累積更新(CU)HTTP/2はOS(HTTP.sys)の実装差が出やすいwinver / systeminfo / Get-HotFix“同じServer 2016”でもパッチ差が数年分ある
IISのモジュール/フィルタ差レスポンス加工・圧縮・セキュリティモジュールがHTTP/2で不正ヘッダーを作ることがある%windir%\system32\inetsrv\appcmd list modules検証だけにEDR/URLフィルタ系が入っている
LB/リバプロ/WAFの有無(TLS終端)HTTP/2⇄HTTP/1.1変換で禁止ヘッダーが混入しやすい通信経路図、証明書の提示元、Serverヘッダー、LB設定“同じドメイン”でも検証だけ別の経路になっている
TLS暗号スイートの順序HTTP/2と相性の悪い暗号順序だとHTTP/2ネゴシエーション自体が破綻する場合があるGPO/IISCrypto/レジストリ、SSLテスト検証だけハードニングを強めている
HTTP.sysのHTTP/2制限レジストリHTTP/2のフレーム乱用対策などで“閾値超え=接続Kill”になり得るHKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parametersセキュリティ対策で値が極端に小さい

TLS暗号順序に関しては、Windows Server 2016で独自順序を入れる場合「HTTP/2互換の暗号を優先し、ブロックリスト相当を後ろへ」という前提があります。HTTP/2ネゴシエーションに失敗したときの回避として、一時的にHTTP/2を無効化する手順(後述のEnableHttp2Tls)もMicrosoftの情報として示されています。

また、HTTP/2のSettingsフレーム乱用による不安定化への対策として、HTTP.sys側で閾値(Http2MaxSettingsPerFrame / Http2MaxSettingsPerMinute)をレジストリで定義できる旨が案内されています。検証環境だけこれらが厳しく設定されていると、意図せず“接続が落ちる”現象に見えることがあります。

RST_STREAMが出る“実務で多い原因”:禁止ヘッダー混入を疑う

HTTP/2は「HTTP/1.1のメッセージをそのまま運ぶ」わけではありません。特に中継があると、次のような“HTTP/2として不正”なものが混ざりやすいです。

接続固有ヘッダーが残る

HTTP/2メッセージにConnectionやKeep-Alive、Transfer-Encoding、Upgrade、Proxy-Connectionが含まれると、メッセージはmalformedとして扱われます。HTTP/1.1→HTTP/2へ変換する中継は、これらを除去しないといけません。

検証環境だけWAF/リバプロでHTTP/2終端している場合、ここが一番の地雷ポイントになります。

ヘッダー名の大文字混入

HTTP/2ではヘッダー名に大文字が含まれてはいけません(HTTP/1.1は大小文字を区別しないので、ここで“HTTP/2だけ壊れる”が起きます)。

Transfer-Encoding: chunkedが残る

HTTP/2ではchunked転送自体が使えません。にもかかわらず中継やフィルタがTransfer-Encoding: chunkedを残すと、HTTP/2として矛盾が生まれ、ブラウザ側でプロトコルエラーに繋がります。

実務的な落としどころ:HTTP/2を無効化してHTTP/1.1に固定する

原因究明に時間がかかる一方で、運用・検証を前に進める必要があるなら、現実的な回避策は「HTTP/2を止める」です。今回のケースでも、HTTP/2を無効化するとXHRが通ることが確認されています。

Windows Server 2016でHTTP/2(TLS)を無効化する手順

Windows Server 2016では、HTTP/2はOSのHTTP.sysが担っており、レジストリでHTTP/2(TLS)を無効化できます。Microsoftの情報として、EnableHttp2Tlsを0/1で切り替える手順が示されています(変更後は再起動が必要)。

レジストリ:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HTTP\Parameters

DWORD:
EnableHttp2Tls = 0  (HTTP/2 over TLS を無効化)
EnableHttp2Tls = 1  (HTTP/2 over TLS を有効化)

反映:
サーバー再起動(または該当サービス再起動)

ポイントは、IISのHTTP/2は「IISのサイト設定でON/OFF」というより、HTTP.sys(OS)側の機能として効いていることです。HTTP/2はIISの新しい専用設定が増えたというより、できるだけ透過的に動く設計で、HTTP/2専用のIIS設定項目は増えていません。

検証環境だけ止めるという運用はアリか

本番と検証でHTTP/2の有無が違うと「本番だけ問題が出るのでは?」という不安は残ります。ただ、今回のように検証環境のHTTP/2が不安定で検証が進まない場合、まずは検証を成立させることが優先です。

  • 検証を回す間はHTTP/1.1固定で“アプリの検証”を先に完了させる
  • 並行して、HTTP/2を有効化した状態の“インフラ検証”を別枠で実施する

この分離は、原因がLB/WAF/パッチ差分にある場合ほど効きます。HTTP/2はネットワーク/中継/OS実装の影響が大きく、アプリ側をいじっても改善しないことがあるからです。

「最大同時ストリーム数を増やす」で回避できるか?

結論から言うと、IIS(というよりHTTP.sys)でSETTINGS_MAX_CONCURRENT_STREAMSを任意の値に“簡単に”上げられる、という話は少なくともIISの標準設定としては前に出てきません。

  • HTTP/2自体の仕様として、SETTINGS_MAX_CONCURRENT_STREAMSは推奨100以上で、100は一般的な水準
  • IISのHTTP/2は透過実装で、HTTP/2専用のIIS設定は増えていない(=「IISマネージャで最大同時ストリーム数を上げる」類の設定が基本的にない)
  • HTTP.sysにはHTTP/2関連のレジストリ制限が複数存在するものの、Microsoftが案内しているのは主に不正挙動(Settingsフレーム乱用)を止めるための閾値設定などで、同時ストリーム数の調整を“正攻法の対策”として提示しているわけではない

もし「同時ストリーム数が関係していそう」という感触があるなら、いきなり“上限を上げる”より先に、次の順で検証するのがおすすめです。

やること狙い観察ポイント
同時リクエスト数を意図的に減らす「負荷・同時数依存」の切り分けAJAX呼び出しを直列化、ページ初期化時の並列処理を抑える
同一ブラウザでタブ数/同時アクセスを変える100近辺での再現性確認タブを増やすほど失敗率が上がるか
中継機器をバイパスして直接IISへ当てるLB/WAF起因の除外直当てでは正常、経路に戻すと再発…なら中継が濃厚

原因調査をIISフォーラム/ベンダに相談するなら、揃えるべき情報

元Q&Aでも「IIS側の問題としてフォーラムへ」という流れでしたが、相談時に情報が揃っているほど解決が早いです。

  • 本番/検証それぞれのOSビルド番号、累積更新(CU)の適用状況
  • IISのサイト設定(バインド、SNI、証明書チェーン、TLS設定)と、TLS終端位置(IIS直かLBか)
  • IISのモジュール一覧(サードパーティフィルタ/圧縮/セキュリティ製品の有無)
  • HTTP.sysのレジストリ設定(EnableHttp2Tls、HTTP/2関連の閾値設定など)
  • 失敗時の通信キャプチャ(RST_STREAMのエラーコード、直前のHEADERS/SETTINGSの流れ)
  • 失敗するのが「特定のASMXだけ」か「POSTだけ」か「サイズ依存」か(再現条件)
  • IISログ(可能ならProtocol Versionを含む)

まとめ:やるべきことは「最大同時ストリーム数の調整」より「差分潰し」と「経路の健全性確認」

HTTP/2でだけnet::ERR_HTTP2_PROTOCOL_ERRORが出て、HTTP/1.1だと通るなら、まず疑うべきはアプリではなくHTTP/2の実装・中継です。特に検証環境だけ再現する場合、OS/IISパッチ差分やLB/WAF/SSL終端の差分が原因になりやすいです。

運用上の落としどころとしては、検証を止めないためにHTTP/1.1固定(HTTP/2無効化)を先に適用しつつ、並行して「禁止ヘッダー混入」「HTTP/2関連レジストリ制限」「更新差分」「中継の有無」を潰していくのが現実的です。IISのHTTP/2は透過実装で専用設定が増えていないため、“最大同時ストリーム数を上げる”方向での即効性は期待しにくく、まずは差分をなくす方が再現性のある解決に繋がります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次