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 Suffix | Value Type | Example |
|---|---|---|
| Regular (no suffix) | ASCII string | x-request-id: abc123 |
-bin | Binary (base64 on wire) | trace-context-bin: [bytes] |
⚠️ Rules
- Keys must be lowercase ASCII, no spaces
- Keys starting with
grpc-are reserved (don't use them) - Binary values MUST use the
-binsuffix - Metadata is NOT encrypted by default (use TLS)
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 Case | Key | Direction |
|---|---|---|
| Authentication | authorization | Client → Server |
| Request tracing | x-request-id | Client → Server |
| Distributed tracing | traceparent | Both |
| Rate limiting | x-ratelimit-remaining | Server → Client |
| Error details | grpc-status-details-bin | Server → Client (trailer) |
| Deadline propagation | grpc-timeout | Client → 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
- Metadata = gRPC's equivalent of HTTP headers
- Three positions: request headers, response headers, response trailers
- Use
-binsuffix for binary values - Don't use
grpc-prefix (reserved) - Use for auth, tracing, request IDs — not business data