ERR_HTTP2_PROTOCOL_ERROR: Meaning & Fix in Chrome and Edge
Key takeaway
ERR_HTTP2_PROTOCOL_ERROR (Chromium error -337) means the HTTP/2 stream carrying the page failed. Either the server, proxy or CDN reset it partway (Chrome reports any such reset as -337), or Chrome rejected headers that break HTTP/2's rules. For visitors, a reload, an Incognito window, clearing the site's cookies or pausing antivirus HTTPS scanning sometimes gets past it, but usually only the site can fix it. For site owners the usual causes are a response the server or proxy couldn't finish (a full disk, an unwritable temp folder, a crashing app), a Content-Length that doesn't match the body, or a forbidden header such as Transfer-Encoding. Reproduce it with curl --http2 -v.
Check it now

Start reading
- What Is ERR_
HTTP2_ PROTOCOL_ ERROR? - What HTTP/2 Treats as Malformed
- What Causes ERR_
HTTP2_ PROTOCOL_ ERROR? - Fix 1: Hard Reload and Try Incognito
- Fix 2: Clear That Site's Cookies and Cached Files
- Fix 3: Pause Antivirus HTTPS Scanning and Extensions
- Fix 4: Update Chrome and Check Other Browsers
- Fix 5: Test the Page Without HTTP/2
- For Website Owners: Fixing HTTP/2 Protocol Errors
- 1. Reproduce It With curl
- 2. Check for Responses Cut Off Mid-Stream
- 3. Fix Content-Length and Forbidden Headers
- ERR_
HTTP2_ PROTOCOL_ ERROR vs Similar Errors - Frequently Asked Questions
What Is ERR_HTTP2_PROTOCOL_ERROR?
ERR_HTTP2_PROTOCOL_ERROR is a Chrome and Edge error meaning the HTTP/2 stream carrying the page failed: the server reset it before it finished, or sent headers the browser rejects. In Chromium it is net error -337: "There is an HTTP/2 protocol error" (net_error_list.h). Chrome has no dedicated error page for it, so you see the generic one: "This site can't be reached. The webpage at … might be temporarily down or it may have moved permanently to a new web address," with ERR_HTTP2_PROTOCOL_ERROR underneath. Edge shows the same code on its own "Hmm… can't reach this page" screen.
HTTP/2 is the faster version of HTTP that most HTTPS sites now use. Each request travels as its own stream inside one connection, and either side can cancel a stream with a reset. Chrome reports any reset from the server as a protocol error, whatever the server's reason, and it also cancels a stream itself when the headers break HTTP/2's rules (a Transfer-Encoding header, capital letters in a header name, a missing status).
Developers often see it in the DevTools console as net::ERR_HTTP2_PROTOCOL_ERROR 200 (OK). That combination is a strong clue: the headers were valid and the server answered 200, but the stream failed while the body was arriving.
See the headers a page really sends
What HTTP/2 Treats as Malformed
The HTTP/2 standard (RFC 9113) lists the mistakes that make a response malformed. These are the problems behind most real-world errors, and how each one reaches the browser:
| Rule | What breaks it |
|---|---|
| Content-Length must equal the body size | A server or plugin declares one size and sends another; the proxy, CDN or server in front then resets the stream |
| The response must end cleanly | The server, proxy or app can't finish (a full disk, a crash, a timeout) and resets the stream; nginx does this when it can't complete a response it has started |
| No connection-specific headers | Transfer-Encoding in an HTTP/2 response (Chrome rejects it); Connection, Keep-Alive, Proxy-Connection and Upgrade are forbidden too, and strict clients such as curl reject them |
| Field names must be lowercase | Header names with capital letters passed straight through to HTTP/2 |
| Valid header values | Newlines or other forbidden characters inside a header value |
| A valid :status pseudo-header | A missing or unreadable status code |
What Causes ERR_HTTP2_PROTOCOL_ERROR?
Most cases come from the server side, but a few things on your own computer can damage a response on its way in:
- On the server: a response the server or proxy can't finish (a proxy that runs out of disk space or can't write its temp files, an app that crashes while streaming, a timeout), wrong Content-Length values that make the proxy in front give up, forbidden or invalid headers, and buggy HTTP/2 support in an older server or CDN configuration.
- On your side: antivirus or firewall software that inspects HTTPS and rewrites traffic, browser extensions that modify requests or responses, a request the server mishandles (for example oversized cookies), and occasionally an outdated Chrome build.
Fix 1: Hard Reload and Try Incognito
Press Ctrl + Shift + R (Mac: Cmd + Shift + R) to reload without the cache. A response that was cut off once may come through fine on the next try.
Then open the page in an Incognito window (Ctrl + Shift + N, Mac Cmd + Shift + N). Incognito has no cookies, no cache from your normal profile, and extensions are off by default. If the page loads there, Fix 2 or Fix 3 will solve it in your normal window.
Fix 2: Clear That Site's Cookies and Cached Files
Clearing the site's cookies and cached files changes the request Chrome sends (fewer cookies, no cached copy to revalidate), which can avoid a server bug. Click View site information at the left of the address bar → Cookies and site data → Manage on-device site data → delete, then reload. For a broader clean, press Ctrl + Shift + Delete and clear Cached images and files for the Last 7 days.
Fix 3: Pause Antivirus HTTPS Scanning and Extensions
Security software that decrypts and inspects HTTPS sits in the middle of every HTTP/2 connection. If its HTTP/2 handling is buggy or out of date, it can break streams or pass on headers Chrome rejects. Turn off only the HTTPS-scanning feature (Avast/AVG: Web Shield → Enable HTTPS scanning; ESET: SSL/TLS protocol filtering; Kaspersky: Scan encrypted connections) and reload. If that fixes it, update the antivirus and add an exclusion for the site.
Next, disable all extensions at chrome://extensions, reload, then turn them back on one at a time. Ad blockers, privacy tools and anything that edits headers are the usual suspects.
Fix 4: Update Chrome and Check Other Browsers
Open chrome://settings/help to install any pending update, then restart the browser. Then open the same page in Firefox or Safari, and on another device or network. If it fails everywhere, the site is broken and only its owner can fix it (antivirus HTTPS scanning affects every browser on one computer, so a second device is the real test). If only Chrome or Edge fails, the site is probably sending a header Chrome rejects; Fixes 2 and 3 are still worth repeating.
Fix 5: Test the Page Without HTTP/2
Chrome can be started with HTTP/2 (and HTTP/3) switched off, which shows whether HTTP/2 is involved. Start a second, separate Chrome from a terminal; the extra profile folder makes it a new instance even while your normal Chrome is open:
# Windows (Command Prompt)
"C:\Program Files\Google\Chrome\Application\chrome.exe" --user-data-dir="%TEMP%\h2test" --disable-http2 --disable-quic
# macOS (Terminal)
open -na "Google Chrome" --args --user-data-dir=/tmp/h2test --disable-http2 --disable-quicIf the page loads in that window, its HTTP/2 responses are the problem and the owner needs to fix them. If it now fails with ERR_CONTENT_LENGTH_MISMATCH or ERR_INCOMPLETE_CHUNKED_ENCODING, the server is cutting the response short whatever the protocol. Close that window when you're done; your normal Chrome is unaffected.
For Website Owners: Fixing HTTP/2 Protocol Errors
If visitors on different networks and browsers report the error, the problem is in your stack. These three checks find almost all of them.
1. Reproduce It With curl
# HTTP/2: watch the end of the output
curl -sv --http2 https://example.com/broken-page -o /dev/null
# A stream the server gave up on ends with something like:
# HTTP/2 stream 1 was not closed cleanly: INTERNAL_ERROR (err 2)
# (curl 8.19+: HTTP/2 stream 1 reset by server (error 0x2 INTERNAL_ERROR);
# "reset by curl" means curl rejected the response itself)
# Same URL over HTTP/1.1 for comparison
curl -sv --http1.1 https://example.com/broken-page -o /dev/nullIf HTTP/1.1 works and HTTP/2 fails, HTTP/2 is involved. "Reset by server" points to the server or a proxy giving up mid-response. curl doesn't check exactly what Chrome checks (it rejects Content-Length mismatches and connection headers Chrome lets through), so treat it as a strong hint, not proof. The HTTP Header Checker lists the headers your app sets, seen from outside your network.
2. Check for Responses Cut Off Mid-Stream
The most common server-side cause is a response that starts (status 200, headers sent) and then can't be finished. nginx buffers larger responses from your app into temporary files, and if the disk is full or nginx can't write to its temp folder, it resets the stream and the browser reports a protocol error. Check:
df -h # is any disk full?
sudo grep -E "No space left|Permission denied" /var/log/nginx/error.log | tail
ls -ld /var/lib/nginx/proxy /var/lib/nginx/fastcgi # Debian/Ubuntu packages
ls -ld /var/cache/nginx/proxy_temp /var/cache/nginx/fastcgi_temp # nginx.org packages
# the temp folders must be writable by the nginx worker userAlso check the app itself: a PHP or Node process that crashes, times out or hits a memory limit while streaming a large page or download produces the same result. App logs around the time of the error usually show it.
3. Fix Content-Length and Forbidden Headers
- Content-Length: let the server calculate it. Don't set it by hand in app code if a plugin, middleware or the web server also compresses or modifies the output, because the declared length then no longer matches the bytes sent. A length set in PHP code while a plugin or PHP setting also compresses the output is one way this happens on WordPress sites.
- Transfer-Encoding: never send it in an HTTP/2 response; Chrome resets the stream when it sees one. RFC 9113 also forbids
Connection,Keep-Alive,Proxy-ConnectionandUpgrade. Chrome ignores those, but curl and other strict clients reject them, and nginx strips most of them from proxied responses for you. - Header values: make sure no header contains a line break or a control character. This often comes from user input placed in a header, such as a filename in
Content-Disposition. - CDN in front: if you use Cloudflare or another CDN, test the origin directly (with curl
--resolveor a hosts entry) to tell whether the error comes from the origin or from the CDN.
ERR_HTTP2_PROTOCOL_ERROR vs Similar Errors
| Error | Code | What happened |
|---|---|---|
| ERR_HTTP2_PROTOCOL_ERROR | -337 | An HTTP/2 response broke the protocol rules |
| ERR_QUIC_PROTOCOL_ERROR | -356 | The same kind of failure over HTTP/3 (QUIC) |
| ERR_SSL_PROTOCOL_ERROR | -107 | The HTTPS (TLS) handshake failed before HTTP/2 started |
| ERR_CONNECTION_CLOSED | -100 | The connection was closed before a page arrived |
| ERR_EMPTY_RESPONSE | -324 | The server sent nothing at all |
Detailed guides: ERR_QUIC_PROTOCOL_ERROR, ERR_SSL_PROTOCOL_ERROR, ERR_CONNECTION_CLOSED and ERR_EMPTY_RESPONSE. To check a site's certificate and HTTPS setup from outside, use the SSL Checker.
See the headers your site really sends
DNS Robot's HTTP Header Checker fetches any URL over HTTP/1.1 from our servers and lists the status code and every response header your app sets (Content-Length, Content-Encoding, Content-Disposition), so you can compare them with what curl --http2 shows.
Sources
- net_error_list.h (HTTP2_PROTOCOL_ERROR -337), Chromium
- RFC 9113: HTTP/2, malformed messages, IETF
- HTTP/2: ERR_HTTP2_PROTOCOL_ERROR, Cloudflare



