Skip to content

Metadata

The request and response messages carry your domain data — a user, an order. But some information is about the call itself: who is calling, which trace this belongs to, what request id to log. Putting that inside every message would pollute your schema. gRPC gives it a separate channel: metadata.

Metadata is a set of key/value pairs sent alongside a call, exactly like HTTP headers — which is literally what they become on the HTTP/2 wire.

Metadata comes in two positions:

  • Headers travel before the message, at the start of the call. This is where a client puts an auth token or a trace id — anything the server needs up front.
  • Trailers travel after the response, at the end of the call. This is where a server puts information known only once work is done — like final status details or server-side metrics. Streaming responses especially rely on trailers, since the status can only be known after the last message.
flowchart LR
  client["client"] -->|"headers: authorization, x-trace-id"| server["server"]
  server -->|"response message(s)"| client
  server -->|"trailers: final status, metrics"| client
Where headers and trailers sit in a call

A client sets headers before the call; the server reads them, and can send back headers and trailers.

// Client: attach metadata to the outgoing call.
ctx := metadata.AppendToOutgoingContext(ctx,
"authorization", "Bearer "+token,
"x-request-id", reqID,
)
resp, err := client.GetUser(ctx, req)
// Server: read incoming metadata.
md, _ := metadata.FromIncomingContext(ctx)
reqID := md.Get("x-request-id")
  • Keys are lowercase. gRPC normalizes metadata keys to lowercase ASCII; treat them case-insensitively.
  • Binary values use a -bin suffix. A key ending in -bin (e.g. trace-context-bin) is treated as raw bytes and base64-encoded on the wire; anything else must be a valid ASCII string.
  • Don’t reuse reserved keys. Keys starting with grpc- are reserved by the framework — don’t set them yourself.
  • Metadata is not encrypted by itself — its privacy comes from TLS on the channel. A token in metadata over an insecure channel is exposed.

Metadata is how context flows through a chain of services. When service A calls B which calls C, you typically propagate the trace id and request id: each hop reads the incoming metadata and copies the relevant keys onto its outgoing calls, usually inside an interceptor so no handler has to remember. That is how a distributed trace stays stitched together across many gRPC hops.

What is metadata used for in gRPC?
What is the difference between headers and trailers?
What does a metadata key ending in `-bin` mean?
How do trace ids stay connected across A → B → C?