skip to content

What does a gRPC call look like as an HTTP/2 request, and how does the server know which method is wanted?

level: juniorimportance: must knowfreq 62%

answer

  1. always POST, never GET
  2. the method name is in the path
  3. package dot service, slash, method
  4. content type begins application/grpc
  5. te: trailers is an old-proxy tripwire

basics

~10 s

Every gRPC call is an HTTP/2 POST whose :path is /{package}.{Service}/{Method}, with content-type application/grpc and te: trailers. The request body carries length-prefixed messages; nothing about the call lives in a query string.

solid answer

~40 s

A gRPC call is one HTTP/2 stream carrying an ordinary POST. The pseudo-headers are `:method POST`, `:scheme http` or `https`, `:authority` for the host, and `:path`, which is `/` + the fully qualified service name + `/` + the method name — so `/customs.v1.DeclarationService/SubmitDeclaration` names both. Beside them the client sends `content-type: application/grpc` (optionally suffixed `+proto` or `+json` to say how the messages are encoded), `te: trailers` to announce it accepts a trailing status, `user-agent`, and optionally `grpc-encoding` and `grpc-accept-encoding` for per-message compression. There is no query string and no path parameter: every argument is a field of the request message in the body. A server that does not recognise the content type answers `415 (Unsupported Media Type)` and the call never starts.

code

http · 12 lines
http
HEADERS (stream 5, END_HEADERS)
:method = POST
:scheme = https
:path = /customs.v1.DeclarationService/SubmitDeclaration
:authority = filing.example
content-type = application/grpc+proto
te = trailers
grpc-accept-encoding = identity, gzip
user-agent = grpc-<impl>/<version>

DATA (stream 5, END_STREAM)
<length-prefixed request message>

go deeper

for a junior

Be able to sketch the request from memory: POST, a path of /package.Service/Method, content-type application/grpc, and messages in the body. That sketch alone answers most first-screen questions about the wire.

for a middle

Explain why the path is the routing key and what that buys an intermediary: per-method routing, authorisation and rate limiting with no message decoding. Know that te: trailers is a compatibility tripwire, not a negotiation.

for a senior

In production you will read this shape out of a capture. Know which failures are HTTP-shaped (415 for a wrong content type, a header block over the limit) and which are gRPC-shaped, because they are diagnosed with different tools.

for a principal

The design bet here is that the call's identity is plaintext and its arguments are opaque. That makes edge policy cheap per method and impossible per field, which is the trade-off to weigh when you set a boundary standard across teams.

## One POST per call gRPC does not invent a transport. A call is an ordinary **HTTP/2 request**: the client opens one stream, sends a `HEADERS` frame, then `DATA` frames holding the request messages, then closes its direction. Because the request shape is plain HTTP/2, anything that speaks HTTP/2 — a reverse proxy, an L7 load balancer, a packet capture — sees the call, which is exactly why this leaf matters: the parts that are *not* ordinary HTTP are the parts an intermediary can ruin. The method is **always POST**. There is no read-versus-write distinction on the wire, no `GET` for a lookup, and no way for an rpc declaration to choose something else. A cache keyed on HTTP method semantics therefore sees every call as unsafe and non-idempotent, which is the correct reading of it. ## The pseudo-header block Four pseudo-headers open the request: - **`:method`** — `POST`, without exception. - **`:scheme`** — `http` or `https`, following whether the connection is protected. - **`:authority`** — the host the caller believes it is talking to; an in-path hop that rewrites it changes which virtual service answers. - **`:path`** — the routing key for the whole call, described next. ## Naming the method in the path The specification builds the path from two names the schema already defines: 1. **Service-Name** is `{proto package name}` `"."` `{service name}` — so a `DeclarationService` declared in package `customs.v1` is `customs.v1.DeclarationService`. 2. The path is `"/"` + Service-Name + `"/"` + the **method name** exactly as the schema spells it. So a broker filing a declaration sends `:path = /customs.v1.DeclarationService/SubmitDeclaration`. This is the single most useful fact about the request shape in operations: an intermediary can route, rate-limit, authorise or log per method **without decoding a single message**, because the method identity is in a plain-text path. It is also why a route pattern written for a resource-oriented API — `/declarations/{id}` — never matches: gRPC paths are not resource paths, and the declaration's identifier is a field inside the request message, not a path segment. ## The call-definition fields Beside the pseudo-headers sit fields the specification defines for the call itself, distinct from any application metadata the caller attaches: | field | sent by | what it does | |---|---|---| | `content-type` | client | `application/grpc`, optionally `application/grpc+proto` or `application/grpc+json`, naming the message encoding | | `te` | client | the value `trailers`, announcing the client accepts a trailing status | | `grpc-encoding` | client | the per-message compression applied to messages in this request | | `grpc-accept-encoding` | client | the per-message compressions the client can decode in the response | | `user-agent` | client | identifies the calling implementation | Header field names are lowercase and drawn from a restricted charset (`0-9`, `a-z`, `_`, `-`, `.`), and the specification suggests an **8 KiB** default limit on the whole request header block, enforceable with `SETTINGS_MAX_HEADER_LIST_SIZE`. A caller that stuffs a large token or a long correlation value into metadata can exceed it, and the call fails before the server sees a byte of the message. ## Why `te: trailers` is there at all On HTTP/1.1 the `TE` field was how a client announced it would accept trailing fields after a chunked body. HTTP/2 needs no such negotiation — trailing fields are just another `HEADERS` frame. gRPC keeps the field anyway as a **tripwire**: the outcome of every call travels in the trailer section, so an intermediary too old to forward one is a fatal hop, and requiring the field on every request gives such a hop something it can recognise and refuse rather than silently swallow. ## What the server checks before anything else 1. **Content type.** If it does not begin with `application/grpc`, the server answers `415 (Unsupported Media Type)`. This is an HTTP-level rejection: there is no gRPC status because there is no gRPC call yet. 2. **Path.** An unrecognised service or method is a normal gRPC failure, answered with a status in the trailing metadata rather than an HTTP error. 3. **Message framing.** Everything after the header block is read as length-prefixed messages, never as a raw body. The practical consequence for a cross-border filing gateway is that the two failure classes look completely different on the wire: a misconfigured client gets an HTTP-shaped rejection it can read with any tool, while a real application failure gets a 200 response whose outcome is hidden in trailing metadata — the subject of the next question.

  • What does a server do when the request's content-type is not application/grpc?
    It answers `415 (Unsupported Media Type)` at the HTTP layer. No gRPC call was ever created, so there is no `grpc-status` to return — which is why this failure reads as an HTTP error in a capture while application failures read as a 200.
  • What do the +proto and +json suffixes on the content type mean?
    They name how each message in the body is encoded. `application/grpc+proto` is binary protobuf, `application/grpc+json` is JSON; bare `application/grpc` is accepted and treated as the default proto encoding. The suffix describes the messages, not the framing around them, which is identical either way.
  • Why is there no query string or path parameter on a gRPC request?
    Every argument is a field of the request message carried in the body, so the path only has to identify the method. That keeps routing trivially cheap for an intermediary but means nothing about the call's arguments is visible without decoding the message.

saying these in an interview costs you the question

  • Thinks gRPC defines its own transport underneath HTTP/2
  • Expects resource-style path segments carrying record identifiers
  • Says read-only methods are sent as GET with query parameters
  • Believes the method name travels inside the request message
  • Treats content-type: application/json as valid for a gRPC call