Migrating some of our internally used services to a new reverse proxy layer i've been struggling with ERR_HTTP2_PROTOCOL_ERROR when accessing a migrated service.
Our reverse proxy layer is running nginx and forwarding to our orchestration layer (in this case docker swarm). External and internal connections are encrypted using https.
The error occured when accessing the migrated services from chromium based browsers (like chrome or edge). Connectivity using firefox has been working fine.
Verifying the connections showed quickly that firefox has just been using http1.1 when connecting whereas chromium used http2.
The error code showed by chromium has been: ERR_HTTP2_PROTOCOL_ERROR
Well, that's not very much. Checking out the nginx logs showed that response has been sent correctly (http 200). Having successful connectivity using http1.1 connections didn't seem that the problem has been located on the upstream.
Getting more details
The key to solve this issue has been to use net-export. This provides the ability to capture events related to network activity and errors occuring there.
Generating the log is straight forward.
- Open a new browser (that should only contain a single tab)
- Open net-export tool: chrome://net-export/ (copy & paste link)
- Start log
- Reproduce the error
- Stop log
Having generated a log, navigate to netlog-viewer (https://netlog-viewer.appspot.com/#import) and open the log file.
This will show the contents of the log.
Navigate to HTTP/2 and find the session of interest.
Clicking the ID will apply a filter on the viewer and lead you to the associated session record.
The actual record data shows relevant information:
Read the trace carefully, there's no highlighting for errors (as of today). You may see that there's an issue shown HTTP2_SESSION_RECV_INVALID_HEADER. This will lead us directly to the actual error: Invalid character 0x0A in header value.
Therefore our solution is to fix the incorrect header that has been set on the upstream (in our case a header has not been terminated correctly) and right afterwards it works flawless.
Why only with http2?
For quite some while i've excluded some components (like headers) as an error source - http1.1 (provided by old access path) has been working fine. And headers are the same, right?
Chromium introduces a more strict header parsing when connecting using http2 (which won't break stuff that has been working before).
Lesson learned: Easiest way to diagnose into connection issues is to use the netlog that may be created using tools provided already with chromium based browsers.