v10.2 Fix layout switching, pane, and high-DPI display issues See what's new

How to Debug HTTP/2 Traffic on Windows

Inspect HTTP/2 connections — multiplexed streams, HPACK headers, and pseudo-headers

  1. Connection 1 TCP + TLS · preface
  2. Streams multiplexed · stream ID
  3. Frames HEADERS · DATA · RST_STREAM
  4. Headers HPACK · :pseudo-headers

HTTP/2 is a binary, multiplexed transport for the same request/response semantics as HTTP/1.1: one TCP connection carries many concurrent streams, headers are compressed with HPACK, and the request line and status line are replaced by pseudo-headers. It is defined in RFC 9113 (header compression in RFC 7541). To debug HTTP/2 traffic you have to read it on the wire — and that is where the trouble starts.

You cannot just tail a connection and read text the way you can with HTTP/1.1. The bytes are framed, the headers are HPACK-compressed against a stateful table, dozens of requests are interleaved on one socket, and the whole thing usually rides inside TLS. A raw packet capture shows you frames and ciphertext, not the GET you are looking for.

Inside an HTTP/2 connection: preface, frames, and streams

An HTTP/2 connection opens with a fixed 24-byte client connection preface (PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n), followed by a SETTINGS frame from each peer. Everything after that is a stream of binary frames. Each frame has a 9-byte header — a 24-bit length, an 8-bit type, 8-bit flags, and a 31-bit stream identifier — followed by its payload. The types you care about when debugging are HEADERS (and its CONTINUATION), DATA, SETTINGS, WINDOW_UPDATE, RST_STREAM, and GOAWAY.

A single request/response lives on one stream, identified by its stream ID (client-initiated streams are odd-numbered). Because HTTP/2 multiplexes many streams over one connection, frames from different requests are interleaved on the socket, and only the stream ID ties a set of frames back to one logical exchange. Here is the decoded, logical view of a simple exchange:

# Connection preface — client sends 24 fixed bytes first
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n

# SETTINGS frame — stream 0, connection-wide parameters
SETTINGS_HEADER_TABLE_SIZE: 4096
SETTINGS_INITIAL_WINDOW_SIZE: 65535
SETTINGS_MAX_FRAME_SIZE: 16384

# HEADERS frame — stream 1, opens the request
# (HPACK-compressed and TLS-encrypted on the actual wire)
:method: GET
:scheme: https
:authority: web.run
:path: /

# HEADERS frame — stream 1, response
:status: 200
content-type: text/html; charset=utf-8

# DATA frame(s) — stream 1, END_STREAM set on the last one
<html body bytes>

HPACK: why HTTP/2 headers aren't human-readable on the wire

HTTP/2 header blocks are compressed with HPACK, and this is the single biggest reason a naive capture shows nothing useful. HPACK encodes each header field as either an index into a table or a literal, and string values are usually Huffman-coded. There are two tables: a fixed 61-entry static table of common headers, and a per-connection dynamic table that both peers update as the connection runs — a literal header "with incremental indexing" is appended to the dynamic table so later frames can reference it by index alone.

The practical consequence: a header block can depend on earlier header blocks from the same connection. Literal fields and static-table indexes can be decoded directly, but any dynamic-table reference only makes sense if the decoder has processed the earlier updates in order. HTTP Debugger's HTTP/2 capture layer applies HPACK decoding for the connection, including Huffman-coded strings and dynamic-table updates, which turns the compressed wire bytes into the readable :method and :path rows you see in the header panes.

Pseudo-headers replace the request and status lines

HTTP/2 has no request line or status line. The method, scheme, authority, and path move into request pseudo-headers (:method, :scheme, :authority, :path), and the response status becomes :status. Pseudo-headers always start with a colon, always come before regular headers, and every regular header name is lowercase. A request that would read GET / HTTP/1.1 with a Host header in HTTP/1.1 is instead four colon-prefixed fields.

One detail that trips up per-stream tools: a response HEADERS block on the wire contains only :status and response headers — it does not repeat which method or path it answers. HTTP Debugger keeps the request and response associated by stream ID, so a captured response is linked to the request it replied to instead of being an orphaned 200.

Why HTTP/2 traffic is hard to debug

  • It is binary and framed. There is no line-oriented text to grep. Headers, body, and control signals are all length-prefixed frames that must be parsed before anything is readable.
  • Headers are stateful. HPACK compresses against a dynamic table that evolves over the connection, so a header block that references the dynamic table cannot be decoded correctly if earlier table updates are missing.
  • Streams are multiplexed. One connection carries many interleaved requests; reconstructing a single exchange means following its stream ID through the frame interleave.
  • It is encrypted. Browser and production HTTP/2 runs over TLS with ALPN h2, so a packet capture is ciphertext unless you can supply TLS keys.
  • Browser tools see only the browser. DevTools shows the tab's own requests, not what a desktop app, a backend service, or a CLI client puts on the wire.

Ways to debug HTTP/2 traffic

Each approach answers a different question. Client tools let you make a request; logs tell you what the server decoded; on-the-wire capture shows what actually crossed the network.

Approach What it shows Setup cost Sees existing app traffic?
Browser DevTools (Network) Decoded headers and body for browser-origin requests, with a protocol column None Browser tab only — not other processes
curl --http2 -v / nghttp -v Frames and decoded headers for calls you craft Low (CLI) No — only the calls the tool itself sends
Network protocol analyzers Raw HTTP/2 frames with HPACK reassembly High — needs TLS session secrets, commonly a keylog file Yes, but only if decryption material is available
Server / proxy logs What the server stack decoded after the fact Redeploy or reconfigure Only services you own
Proxy-less HTTP Debugger capture Logical HTTP/2 exchanges reassembled per connection and stream, HPACK decoded No proxy or keylog; HTTPS decryption requires the local certificate to be trusted Yes — any process on the machine

A network protocol analyzer is excellent once it can decrypt, but the gating problem is decryption material: a browser can export SSLKEYLOGFILE, while most desktop apps, services, and runtimes cannot, so the capture stays encrypted. What none of the client tools answer well is "what is my already-running app actually sending over HTTP/2 right now?" That requires capturing the real traffic and reconstructing the HTTP/2 exchanges from the connection.

Debug HTTP/2 traffic with HTTP Debugger

HTTP Debugger captures HTTP/2 traffic from any process on the machine without a proxy, decrypts HTTPS with its own local root certificate, and decodes HPACK so headers are readable. It identifies an HTTP/2 connection by the client preface on the wire and records the negotiated ALPN token, the per-connection Connection ID, and each request's Stream ID alongside the rest of the session.

  1. Reproduce the traffic in the client application or browser. HTTP Debugger captures each request as a grid row, and the Version column shows HTTP/2 for streams negotiated over h2. The method, URL, status, and content type read exactly as they would for HTTP/1.1 — the framing and HPACK are already decoded — so you work with requests, not frames.
    HTTP Debugger main grid with the Version column showing HTTP/2 for several captured requests
  2. A flat list hides which requests shared a connection. Right-click the grid and choose HTTP/2 Connection Tree View to regroup the session by connection: each parent row is one HTTP/2 connection (for example Connection #9386 [HTTP/2] web.run) and its children are the individual streams that were multiplexed over it, each with its request target and status. This is how you see the multiplexing directly — every stream that rode the same TCP connection, in one place.
    HTTP Debugger grid context menu with the HTTP/2 Connection Tree View item highlighted HTTP Debugger Connection Tree View grouping requests under HTTP/2 connection parent rows for web.run and reqbin.com
  3. Select a request and open the Summary pane. Its Connection section is one place to read the HTTP/2 details for that request:

    • ProtocolHTTP/2, with the negotiated ALPN token shown when it differs.
    • Connection ID — the connection this stream rode on; every multiplexed request on that socket shares the value.
    • Stream ID — the individual HTTP/2 stream that carried this request.
    HTTP Debugger Summary pane showing Protocol HTTP/2, Connection ID 9386, and Stream ID 139
  4. Open the Request and Response header panes. HTTP Debugger lists the HPACK-decoded fields with the pseudo-headers first: the request shows :method, :authority, :scheme, and :path above the regular lowercase headers, and the response shows :status. Because the fields are fully decoded, you can filter, copy, and compare them like any other request — the place to confirm an :authority mismatch or a missing authorization header.
    HTTP Debugger Request Details pane showing decoded HTTP/2 pseudo-headers :method, :authority, :scheme, and :path HTTP Debugger Response Details pane showing the HTTP/2 :status pseudo-header and decoded response headers

Modifying HTTP/2 traffic

Capture is read-only by default. For regular HTTP/2 request/response traffic, HTTP Debugger can also update decoded headers and write valid HTTP/2 header blocks back onto the wire. Many header modification rules that apply to HTTP/1.1 also apply to HTTP/2, with the protocol's pseudo-header restrictions:

  • Modify Headers — add, change, or drop request and response HTTP headers on matching HTTP/2 traffic, with HTTP/1.1 Host rules mapped to :authority where appropriate.
  • Auto-Reply — return a canned response (custom status, headers, body, a local file, or a redirect) for matching requests without the server ever being hit.
  • Redirect Connections — send matching host/port traffic somewhere else, for example pointing a production hostname at a local build.
  • Edit & resubmit — change a captured request and send it again; the built-in submitter has an explicit HTTP/2 mode, so you can replay a tweaked request over h2 rather than downgrading it to HTTP/1.1.

Limitations

On-the-wire capture reconstructs the logical HTTP exchange from captured traffic, decrypted and decoded, but it reads HTTP/2 at the protocol layer, not your application's schema. Capture is Windows-only. gRPC and WebSocket streams over HTTP/2 are inspected read-only rather than modified. HTTP/3 over QUIC is a different, UDP-based transport and is not captured. For everything around the requests — timing, sizes, cookies, and the raw HTTP protocol exchange — the HTTP analyzer and per-request panes cover the rest of the HTTP traffic picture. The same workflow extends to the real-time protocols layered on top of HTTP/2 — see the guides on debugging gRPC calls, WebSocket connections, and SSE streams.

Common HTTP/2 debugging scenarios

"Everything is on one connection — which request is which?"

HTTP/2 reuses a single connection for many requests, so a flat timeline blurs them together. Switch to the Connection Tree View and read the Connection ID and Stream ID from the Summary pane: each logical exchange is one stream, and the tree shows exactly which streams shared a connection and in what order they opened.

"My capture tool shows a header that is empty or missing"

That is often an HPACK state problem. If a tool starts decoding mid-connection, or drops a frame that updates the dynamic table, later indexed fields can resolve to the wrong value or nothing at all. You need a tool that keeps the HPACK state for the connection in order — capturing from the start of the connection is the most reliable way to keep the header table correct.

"It works in the browser but fails from my service"

Browser DevTools cannot see a request made by a background service, a desktop client, or one backend calling another. Capture system-wide instead, then compare the failing call's pseudo-headers and :authority against a working one — HTTP/2 routing and virtual hosting keys off :authority, and a wrong value is a common cause of a request that "should" match but does not.

FAQ

How do I see which HTTP/2 stream a request used?

Select the request and read the Connection ID and Stream ID from the Summary pane's Connection section. All requests multiplexed over the same socket share the Connection ID, while the Stream ID is unique to each exchange. The HTTP/2 Connection Tree View groups the grid by connection so you can see every stream that shared one.

Why are HTTP/2 headers lowercase and prefixed with a colon?

HTTP/2 requires lowercase header field names and replaces the request and status lines with pseudo-headers that start with a colon — :method, :scheme, :authority, and :path on the request, and :status on the response. They must appear before regular headers in the block.

Can I see HTTP/2 traffic with a network protocol analyzer?

Yes, but only after decryption. A network protocol analyzer parses HTTP/2 frames and reassembles HPACK, yet production HTTP/2 usually runs over TLS, so it needs TLS session secrets to read application data. A browser can export a keylog file; most desktop apps, services, and runtimes cannot, which is when a system-wide capture that decrypts with its own local certificate is the practical option.

Can I modify HTTP/2 requests, not just inspect them?

Yes, for regular HTTP/2 request/response traffic. HTTP Debugger can modify headers, auto-reply, redirect connections, and edit and resubmit a captured request over HTTP/2. Header changes follow HTTP/2 pseudo-header rules, and gRPC and WebSocket streams over HTTP/2 are inspected read-only.

Does HTTP Debugger support HTTP/3?

No. HTTP/3 runs over QUIC on UDP and is a separate transport from HTTP/2's TCP framing. HTTP Debugger captures HTTP/1.x and HTTP/2; HTTP/3 is not supported yet.

HTTP Debugger Pro

Windows 11 / 10

A proxy-less HTTP/HTTPS sniffer for Windows that captures traffic system-wide from any process.

Download free 7-day trial

No registration required · Trusted since 2007