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

How to Debug gRPC Requests (HTTP/2 & Protobuf)

Inspect gRPC calls over HTTP/2 — streams, framed messages, metadata, and status

gRPC is a high-performance RPC framework that serializes messages with Protocol Buffers and carries them over HTTP/2. To debug gRPC requests you cannot just read a URL and a JSON body the way you do with REST — the method lives in an HTTP/2 path, the payload is binary Protobuf wrapped in a length-prefixed frame, and the real outcome is reported in trailers after the response, not in the HTTP status line. The sections below cover what a gRPC call actually looks like on the wire, the practical ways to observe it, and how to inspect live gRPC traffic without standing up a proxy.

What it takes to debug gRPC requests

A gRPC call is a single HTTP/2 stream. The request opens the stream with a HEADERS frame, sends one or more messages in DATA frames, and the server replies with its own HEADERS, DATA, and a final trailing HEADERS frame. Because HTTP/2 multiplexes many streams over one TCP connection, frames from different calls are interleaved on the wire and only the stream ID ties them back together.

The request line you know from HTTP/1.1 is replaced by HTTP/2 pseudo-headers. The RPC method is the :path, formatted as /package.Service/Method, and the content type is application/grpc. Here is the logical, decoded view of a unary call — the bytes are HPACK-compressed and TLS-encrypted on the actual wire:

# HEADERS frame — request opens the stream
:method: POST
:scheme: https
:path: /routeguide.RouteGuide/GetFeature
:authority: api.example.com:443
content-type: application/grpc
grpc-accept-encoding: gzip, identity
te: trailers

# DATA frame — one length-prefixed gRPC message
# byte 0      : compression flag (0 = none, 1 = compressed)
# bytes 1..4  : message length, big-endian uint32
00 00 00 00 12  <18 bytes of Protobuf>

# HEADERS frame — response
:status: 200
content-type: application/grpc

# HEADERS frame with END_STREAM — gRPC trailers
grpc-status: 0
grpc-message:

The length-prefixed message framing

Every gRPC message inside a DATA frame is wrapped in a 5-byte prefix: one byte for the compression flag (0 for none, 1 for compressed) followed by a big-endian 32-bit length. A single DATA frame can contain several concatenated length-prefixed messages, and one logical message can also span multiple frames. To read a payload you decode the prefix, slice out exactly that many bytes, and — if the compression flag is set — inflate it using the algorithm named in grpc-encoding (typically gzip or deflate). Only then do you have the raw Protobuf bytes.

Status lives in the trailers, not the HTTP status

This is the single most common source of confusion. The HTTP/2 response almost always carries :status: 200, even when the RPC failed. The real result is the grpc-status code, usually in the trailing HEADERS frame, with an optional human-readable grpc-message. Some failures return a trailers-only response, where grpc-status appears in the only response HEADERS block with END_STREAM. A call that returns HTTP 200 but grpc-status: 5 is a NOT_FOUND error, not a success. The numeric codes are fixed:

CodeNameTypical cause
0OKSuccess
3INVALID_ARGUMENTBad request payload or field
4DEADLINE_EXCEEDEDCall exceeded its timeout
5NOT_FOUNDRequested entity does not exist
7PERMISSION_DENIEDCaller lacks permission
12UNIMPLEMENTEDMethod/service not registered (often a routing or version mismatch)
14UNAVAILABLEServer unreachable, connection dropped, or overloaded
16UNAUTHENTICATEDMissing or invalid credentials metadata

Why gRPC requests are hard to debug

  • The payload is binary. Protobuf has no field names on the wire — just tag numbers and bytes — so you cannot eyeball it like JSON without the .proto schema.
  • It is encrypted. Production gRPC runs over TLS, so a plain packet capture shows only ciphertext unless you can decrypt it.
  • Streams are multiplexed. One connection carries many interleaved calls; reconstructing a single request/response pair means following its stream ID.
  • Status hides in trailers. Tools that only show HTTP status will report success on a failed call.
  • Client tools only see their own calls. A CLI client tells you what it sends, not what your already-running service or app is actually putting on the wire.

Ways to debug gRPC requests

Each approach answers a different question. Active clients let you make a call; logging and tracing tell you what the process did; on-the-wire capture shows what actually crossed the network.

Approach What it shows Needs .proto? Sees existing app traffic?
Server logging / interceptors Decoded messages and status from inside your code No (you have the types) Only services you own and can redeploy
grpcurl / Postman Calls you craft, with field-level Protobuf decoding via reflection or .proto Yes, unless server reflection is on No — only the calls the client itself sends
Env tracing (GRPC_TRACE, Channelz) Internal channel/connection state and verbose library logs No Only processes you can configure
Wireshark Raw HTTP/2 frames and gRPC messages Yes, plus a TLS keylog file to decrypt Yes, but with heavy setup
Proxy-less on-the-wire capture Live decrypted calls reassembled per stream, with status and metadata decoded No (raw payload bytes today) Yes — any process on the machine

For services you own, structured logging interceptors with correlation IDs are the cheapest win. To probe an endpoint by hand, grpcurl is the fastest tool — enable server reflection in development so it can introspect the service without local .proto files:

grpcurl -d '{"latitude": 409146138, "longitude": -746188906}' \
  api.example.com:443 routeguide.RouteGuide/GetFeature

What none of those tools do well is answer the question "what is my running browser, desktop app, or microservice actually sending right now?" That requires capturing the real traffic, already decrypted, off the wire.

Debug gRPC requests with HTTP Debugger

HTTP Debugger is a proxy-less HTTP/HTTPS sniffer for Windows that captures traffic system-wide from any process — browsers, desktop apps, CLI tools, local servers, and microservices. Because it sets no system proxy, it sees HTTP/2 gRPC traffic next to regular HTTP/1.1 with nothing to reconfigure. It decrypts HTTPS with a local root certificate, so there is no TLS keylog file and no per-app proxy setting to manage.

  1. , install it, and trust the local root certificate so HTTPS — and TLS-encrypted gRPC — is decrypted. Capture starts automatically.
  2. Run the app that makes the calls and reproduce the request. In the main grid, use the type quick filter to narrow traffic to gRPC and cut out unrelated HTTP, JSON, and static content. HTTP Debugger type filter dropdown with DATA, GENERAL, and STREAMING groups, including a gRPC entry
  3. Select a call to open the gRPC Messages pane. The Info tab shows the decoded call: protocol (HTTP/2), connection and stream IDs, the RPC as service/method, request and response message counts, request/response metadata, and the grpc-status mapped to its name. Each message row carries direction, time offset, byte size, and compression flag. HTTP Debugger main grid with a gRPC call selected — POST over HTTP/2 to hello.HelloService, status 200 / gRPC OK, type application/grpc gRPC Messages pane Info tab showing hello.HelloService/BidiHello over HTTP/2, connection ID 34736, stream ID 1, and per-message request details
  4. Switch between the Info, Text, Raw, and Hex tabs to read a message. Text and JSON payloads render directly; JSON is pretty-printed. Binary Protobuf and compressed-but-undecodable messages are shown as a hex dump with their byte size, and the search box filters messages by payload content. gRPC Messages pane Text tab showing a decoded streaming message payload
  5. Check the decoded grpc-status and grpc-message from the trailers, and walk the per-message offsets for streaming calls to see message ordering and timing. This is where an HTTP-200-but-failed call becomes obvious.

Common gRPC debugging scenarios

"The HTTP response is 200 but the client reports an error"

The transport succeeded; the RPC did not. Read the grpc-status from the trailers. A 12 UNIMPLEMENTED usually means the method name or package in :path does not match what the server registered — often a stale stub or a versioning mismatch. A 16 UNAUTHENTICATED points at missing or malformed credentials in the request metadata.

"Calls intermittently fail with DEADLINE_EXCEEDED"

Use the per-message time offsets to see how long the server took before the deadline fired. If the request messages were sent quickly but the first response message arrives late, the bottleneck is server-side processing, not the client deadline being too aggressive.

"A message body is empty or garbled"

Check the compression flag and grpc-encoding. If a peer sends compressed messages with an encoding the other side did not advertise in grpc-accept-encoding, decoding fails. Seeing the raw framing next to the metadata makes the mismatch immediate.

"Auth works in one client but not another"

Compare the request metadata between the two calls. gRPC credentials travel as metadata headers (for example authorization); capturing both calls shows exactly which header is present, missing, or differently formatted.

Common mistakes when debugging gRPC

  • Trusting the HTTP status code instead of grpc-status — the transport can succeed while the RPC fails.
  • Trying to read Protobuf as if the 5-byte length prefix were part of the message. Strip the prefix first.
  • Forgetting the compression flag and trying to parse compressed bytes directly.
  • Capturing TLS traffic with no decryption in place and concluding "there is nothing there" when it is simply ciphertext.
  • Assuming a CLI client reproduces production exactly — it shows what the client sends, which may differ from your real app's metadata, encoding, or message order.

Limitations and complementary tools

On-the-wire capture shows you exactly what crossed the network, but it does not own your schema. Today HTTP Debugger displays binary Protobuf as raw bytes (hex) rather than decoding it into named fields; text and JSON-codec payloads render directly. For field-level decoding, pair the capture with grpcurl or Postman and your .proto files. gRPC traffic in HTTP Debugger is read-only — request modification and auto-reply rules apply to regular HTTP/HTTPS traffic, not gRPC streams. For everything around the call — headers, timing, sizes, and the raw HTTP/2 protocol exchange — the HTTP analyzer and per-request streaming inspection cover the rest of the picture.

FAQ

Can I see gRPC requests in browser dev tools?

Only partially, and mainly for browser-originated gRPC-Web traffic. Browser dev tools do not decode a native gRPC client's traffic — the full HTTP/2 stream, binary Protobuf framing, and trailers. For native gRPC, use a tool that captures the client process, reassembles the stream, and decodes the framing.

How do I decode a Protobuf message without the .proto file?

You cannot fully reconstruct field names without the schema, because Protobuf puts only tag numbers and wire types on the wire. You can still read raw bytes and partially infer structure from the tags. For named-field decoding you need the .proto file or server reflection, used through a tool such as grpcurl.

Can Wireshark decode gRPC traffic?

Yes, with setup. Wireshark dissects gRPC and Protobuf, but for TLS-encrypted traffic you must supply a TLS keylog file, mark the ports as HTTP/2 via Decode As, and point it at your .proto files for field decoding. It is powerful for low-level frame analysis but heavier to configure than a capture tool that decrypts and reassembles automatically.

Why does my gRPC call return HTTP 200 but still fail?

Because gRPC reports its result in grpc-status, not the HTTP status. The HTTP/2 response is normally 200 regardless of the RPC outcome. A non-zero grpc-status (for example 5 NOT_FOUND or 16 UNAUTHENTICATED) is the actual error, whether it appears in trailers or in a trailers-only response.

Can I capture gRPC traffic without a proxy?

Yes. HTTP Debugger captures gRPC over HTTP/2 system-wide from any process on Windows without configuring a system proxy, and decrypts HTTPS using its local root certificate, so you see live calls reassembled per stream with status and metadata decoded.

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