Geonode logo
Secure connection errors

How to Fix ERR_HTTP2_PROTOCOL_ERROR in Chrome

One check for visitors, then the header and reset rules Chrome enforces, with fixes for the server you run.

Updated

TL;DR

ERR_HTTP2_PROTOCOL_ERROR means the site's server, or a CDN or proxy answering for it, sent an HTTP/2 response Chrome refuses or cut it off with a stream reset. If it fails on every device, only the site's owner can fix it, by correcting headers or the failure behind the reset.

What ERR_HTTP2_PROTOCOL_ERROR looks like in Chrome and other clients

Between heading and code, Chrome suggests the site may be down, although its server did answer.

WhereWhat you see
Chrome error page, headingThis site can't be reached
Chrome error page, code under the summaryERR_HTTP2_PROTOCOL_ERROR
Chrome DevTools Console, page or file cut offFailed to load resource: net::ERR_HTTP2_PROTOCOL_ERROR
Puppeteer page.goto()net::ERR_HTTP2_PROTOCOL_ERROR at https://example.com
Playwright page.goto()page.goto: net::ERR_HTTP2_PROTOCOL_ERROR at https://example.com/
curl --http2, server reset the stream (exit code 92)curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
Node.js http2 clientError [ERR_HTTP2_STREAM_ERROR]: Stream closed with error code NGHTTP2_PROTOCOL_ERROR

Why this happens

The site's server broke HTTP/2's rules in its reply, or abandoned the reply midway.

Chrome checks each HTTP/2 response against the protocol's rules and drops one that breaks them. It also shows this code for nearly every stream reset the server sends, whatever error code the reset carries.

Diagnose your ERR_HTTP2_PROTOCOL_ERROR first

The first check needs only a second device; the rest are for whoever runs the site.

  • Load the page on another device on the same Wi-Fi; if it fails there too, your browser and computer are not the cause.

  • If the page loads blank or broken, open the DevTools Console; this code next to a file means that file's response broke.

  • Run curl -sv --http2 --max-time 15 on the URL; 'Invalid HTTP header field was received' names the header that broke the reply.

  • If curl prints 'was not closed cleanly' plus an error name, the server abandoned the reply rather than sending a bad header.

  • Repeat that curl test with --http1.1; if it succeeds, the problem lies in how your server, CDN or proxy speaks HTTP/2.

Solutions ranked by effectiveness

Start with the headers, then the resets; the last fix buys time while you search.

  1. Most common fix

    Send only headers Chrome accepts over HTTP/2

    Applies when curl names a rejected header or every visitor fails on the same pages. Change the app code or proxy rule that produces the header, then reload those pages.

    1. Use lowercase header names, such as x-request-id rather than X-Request-ID.

    2. Drop transfer-encoding: chunked; HTTP/2 carries the body in DATA frames instead.

    3. Remove line breaks and other control characters from header values.

    4. Keep all response headers, Set-Cookie included, below Chrome's 256 KiB limit.

  2. Check next

    Find out why the server resets streams

    Applies when curl reports a stream that was not closed cleanly. The server gave up on the reply, so find what stopped it.

    1. On nginx, set the error_log level to info; the default level, error, skips client timeouts.

    2. Reproduce the failure and note its exact time.

    3. Fix what each layer's error log, CDN to app, records at that moment.

  3. If nothing else works

    Serve HTTP/1.1 until HTTP/2 is fixed

    A stopgap while the cause stays hidden. Chrome speaks HTTP/2 over TLS only when the server picks h2 in the handshake, so turning that off moves visitors to HTTP/1.1.

    1. In nginx, delete the http2 parameter from every listen line on that port.

    2. On nginx 1.25.1 or later, also set http2 off; in that server block.

    3. Reload, then confirm that curl -sI --http2 prints an HTTP/1.1 status line.

Stop the ERR_HTTP2_PROTOCOL_ERROR from coming back

Checks that catch a broken HTTP/2 reply before many visitors meet it.

  1. Run curl --http2 --max-time 15 on key pages after each release or CDN change; fail on any non-zero exit.

  2. Run headless Chrome in that check too; in our test, curl and Node.js accepted a header line break Chrome rejected.

  3. Collect failures from real visitors: a NEL policy with a Report-To group makes Chrome send h2.protocol.error reports.

Traffic passed on as sentGeonode's SOCKS5 proxies pass your traffic along without altering the data.
Try residential proxies

Related errors

Learn more

FAQ

It is Chromium's network error -337, 'There is an HTTP/2 protocol error': the secure connection worked, but the HTTP/2 exchange inside it broke, through a response Chrome refused or a stream the server reset.

Visitors cannot repair the site's server, so report the error to its owner with the page address and the time. Site owners correct the response headers or the server failure that cut the reply short.

A forward proxy that tunnels HTTPS with CONNECT only relays encrypted bytes, so HTTP/2 runs between Chrome and the site; our tunnelled test showed the same error. A tunnel cut mid-reply gave ERR_CONNECTION_CLOSED instead.

When nginx ends a stream before its response is complete, it sends a reset: PROTOCOL_ERROR after a client timeout or a return 444 rule, INTERNAL_ERROR otherwise. Chrome shows this one code for both.

curl's HTTP/2 library found a header HTTP/2 forbids and names it. Connection and keep-alive trigger it too, yet Chrome loaded our test page with both, so that message alone doesn't prove Chrome fails.

No. ERR_SSL_PROTOCOL_ERROR means the secure connection itself failed to set up. This error comes later, after the TLS handshake has already agreed on HTTP/2, so certificates and TLS versions are not the cause.

Many requests, one proxy port

Geonode's docs say thousands of requests can share one port, with no slowdown or conflicts.