Chapter 10: Metadata, Headers, and Trailers

Metadata in gRPC is like HTTP headers — key-value pairs sent alongside RPCs for cross-cutting concerns (auth tokens, request IDs, tracing info) without polluting your message definitions.

Three Points Where Metadata Flows

Client Server │ │ │─── Initial Metadata (request headers) ──────►│ │ • auth token │ │ • request-id │ │ • grpc-timeout │ │ │ │◄── Initial Metadata (response headers) ──────│ │ • server-version │ │ • rate-limit-remaining │ │ │ │ ... DATA frames (RPC body) ... │ │ │ │◄── Trailing Metadata (trailers) ─────────────│ │ • grpc-status │ │ • grpc-message │ │ • request-cost │ │ │

Metadata Types

Key SuffixValue TypeExample
Regular (no suffix)ASCII stringx-request-id: abc123
-binBinary (base64 on wire)trace-context-bin: [bytes]
⚠️ Rules

C++ Examples

Client: Sending Metadata

ClientContext context;

// Add metadata to outgoing request
context.AddMetadata("authorization", "Bearer my-token-123");
context.AddMetadata("x-request-id", "req-456");
context.AddMetadata("x-trace-bin", binary_trace_data);  // -bin suffix = binary

Status status = stub->GetUser(&context, request, &response);

// Read server's initial metadata (sent before response body)
auto server_metadata = context.GetServerInitialMetadata();
auto it = server_metadata.find("server-version");
if (it != server_metadata.end()) {
  std::cout << "Server version: " << it->second << std::endl;
}

// Read trailing metadata (sent after response body)
auto trailers = context.GetServerTrailingMetadata();

Server: Reading and Sending Metadata

Status GetUser(ServerContext* context,
              const GetUserRequest* request,
              User* response) {
  // Read client's metadata
  auto metadata = context->client_metadata();
  auto auth = metadata.find("authorization");
  if (auth == metadata.end()) {
    return Status(StatusCode::UNAUTHENTICATED, "No token");
  }

  // Send initial metadata back to client
  context->AddInitialMetadata("server-version", "1.2.3");

  // Send trailing metadata (sent with status)
  context->AddTrailingMetadata("request-cost", "42ms");

  // ... process request ...
  return Status::OK;
}

Common Metadata Use Cases

Use CaseKeyDirection
AuthenticationauthorizationClient → Server
Request tracingx-request-idClient → Server
Distributed tracingtraceparentBoth
Rate limitingx-ratelimit-remainingServer → Client
Error detailsgrpc-status-details-binServer → Client (trailer)
Deadline propagationgrpc-timeoutClient → Server (auto)
💡 Metadata vs Message Fields

Use metadata for cross-cutting concerns that apply to ALL RPCs (auth, tracing, request IDs). Use message fields for data specific to one RPC. Rule of thumb: if an interceptor/middleware needs it, it's metadata. If business logic needs it, it's a message field.

Key Takeaways