Why can a browser page not call a gRPC service directly, and what does gRPC-Web change so it can?
answer
- the browser is missing something
- outcome arrives after the body
- trailing fields never reach script
- status re-encoded into the body
- two call shapes do not survive
basics
~20 sgRPC-Web exists because browser code cannot control HTTP/2 framing or read trailing metadata. It carries a call as an ordinary HTTP request whose response body ends with the trailing status, and a translating layer converts to native gRPC.
solid answer
~50 sNative gRPC delivers a call's outcome in trailing metadata, sent after the response body, and it assumes the caller drives HTTP/2 streams directly. Browser networking APIs give page script neither: they expose response headers but not trailing fields. gRPC-Web is a second wire protocol for the same calls, built only from what an ordinary HTTP client already has. The request is a plain `POST` with the encoded message in the body; the response carries the same status fields as a final frame *inside* the body, so the page can read them; the media type is `application/grpc-web`, which a receiver treats as `+proto` when no message-format suffix is given. The expected deployment puts a translating intermediary between the browser and a native gRPC service. The price is that only unary and server-streaming survive - client-streaming and bidirectional streaming are not available to a browser client.
code
http · 13 linesPOST /vet.dispensary.v1.StockService/GetItem HTTP/1.1
host: dispensary.example
content-type: application/grpc-web+proto
accept: application/grpc-web+proto
x-user-agent: grpc-web-javascript/0.1
[ 00 ][ 00 00 00 09 ] <9 bytes: encoded request message>
HTTP/1.1 200 OK
content-type: application/grpc-web+proto
[ 00 ][ 00 00 00 2A ] <42 bytes: encoded response message>
[ 80 ][ 00 00 00 0F ] grpc-status:0<CRLF>go deeper
Remember the one-line reason: the browser cannot read the trailing metadata where a native gRPC call puts its outcome, so the variant moves that outcome into the response body. Be able to name the media type application/grpc-web.
Explain the mechanism rather than the headline: which part of the call moves, into what, and that the response becomes an ordinary HTTP message so the variant no longer depends on HTTP/2 framing. Name the two call shapes that are lost.
Show you know where the translation lives in a real deployment and what follows from it - an extra hop in the path for browser traffic, a response whose end is the end of a body, and an HTTP status line that says nothing about whether the call succeeded.
The angle worth arguing is whether browser reach should be bought with a second wire protocol at all, versus exposing a resource-oriented API for the browser and keeping the typed protocol strictly internal - a question about how many contracts the organisation maintains.
## The problem gRPC-Web solves gRPC is defined over HTTP/2, and a native call depends on two things page script in a browser does not have. The first is the framing layer. The native protocol assumes the caller can drive HTTP/2 streams directly, and browser networking APIs deliberately hide that layer - script hands over a request and receives a response, and the connection underneath is the browser's business. The second is decisive: **trailing metadata**. Native gRPC announces a call's outcome *after* the response body, in a trailer section - a block of header fields sent at the end of the message rather than at the front. The browser's networking APIs surface response headers to script; they do not surface trailing fields. A call whose success or failure is announced only in trailers is, from the page's point of view, a call with no outcome at all. gRPC-Web is a **separate wire protocol for the same calls**, defined so that everything it needs is something an ordinary HTTP client already has: a request with a body, a response with a body, and headers at the front. ## What the variant changes - The call is an ordinary HTTP request: `POST`, a path built from the service and method, and the encoded request message in the body. - **The trailing status moves into the response body** as a final frame, so the outcome arrives as payload bytes the page can read rather than as trailing header fields it cannot. - **The response ends when the body ends.** No protocol frame closes it. - Nothing in the variant depends on HTTP/2 framing, so it also runs over HTTP/1.1 - and it therefore gives up the signals HTTP/2 framing would have carried: stream identifiers, a connection-closing frame, transport pings. - The client identifies itself in a field of the variant's own, `X-User-Agent` (for example `X-User-Agent: grpc-web-javascript/0.1`), because `user-agent` belongs to the browser and script cannot set it. - Two of the four call shapes do not survive. A browser client gets **unary** and **server-streaming**; **client-streaming** and **bidirectional streaming** are not available. ## The media types | Value | What it means | |---|---| | `application/grpc-web` | The variant with no message-format suffix; a receiver treats it as `+proto` | | `application/grpc-web+proto` | The variant, messages encoded with Protocol Buffers - the ordinary case | | `application/grpc-web-text` | The same protocol with the whole body base64-encoded, for environments that cannot carry binary bodies | A client asks for the text form with `Accept: application/grpc-web-text`. Note what is *not* here: the variant defines no cross-origin field names of its own. A browser's usual cross-origin rules apply to a gRPC-Web request exactly as they apply to any other request, and nothing in the protocol changes or replaces them. ## Where the translation happens The expected deployment is a **translating intermediary** sitting between the browser and a service that speaks native gRPC. In one direction it takes the gRPC-Web request and re-frames it as a native call; in the other it takes the native response, lifts the trailing status out of the trailer section and writes it back into the response body as the final frame. A server framework may instead implement the variant itself and serve both protocols on the same port - either way, *something* performs that translation, because a service that only speaks the native protocol will not accept a gRPC-Web request unchanged. One consequence is worth internalising early: the HTTP status of a gRPC-Web response is not the call's outcome. A response can carry an ordinary success status line and still describe a failed call, because the real status is in the trailer frame at the end of the body. ## What an interviewer is checking 1. Do you know that the browser limitation is about **trailing metadata and framing**, not about TLS, ports or performance? 2. Do you know that gRPC-Web is a **different wire protocol**, not gRPC with a flag flipped - so something in the path has to translate? 3. Do you know **what it costs**: two call shapes, and a stream whose end is the end of a body rather than a protocol event? A candidate who answers "the browser just can't do HTTP/2" is close but has the wrong mechanism - browsers speak HTTP/2 constantly. What they do not do is hand script the controls, or the trailers.
- Which request field does a gRPC-Web client use to identify itself, and why not `user-agent`?The variant defines `X-User-Agent`, carrying a value such as `grpc-web-javascript/0.1`. The browser owns `user-agent` and page script cannot set it, so the variant reserves a field of its own for the client library's identity. A server or an intermediary that wants to know which client it is talking to reads that field instead.
- A gRPC-Web response arrives with `content-type: application/grpc-web` and no suffix. What message format should the receiver assume?Protocol Buffers. The bare media type implies the `+proto` form, so a receiver decodes the frames in the body exactly as it would for `application/grpc-web+proto`. The suffix exists to let another message format be named explicitly; its absence is not ambiguity, it is the default.
- Does a gRPC-Web deployment always need a separate translating process?No. A server framework may implement the variant directly and answer both protocols, in which case there is no separate hop. What is unavoidable is the translation itself: a service that speaks only the native protocol will not read a gRPC-Web request, because the framing of the trailing status differs between the two.
saying these in an interview costs you the question
- Says gRPC-Web is native gRPC with a configuration flag switched on
- Claims a browser cannot use HTTP/2 at all, so gRPC is impossible
- Thinks page script can read trailing header fields if configured correctly
- Believes all four gRPC call shapes work from a browser client
- Reads the HTTP status line of the response as the call's outcome
- Assumes a native-only gRPC server accepts a gRPC-Web request unchanged