Skip to main content
gRPC-Web

gRPC-Web

Active

gRPC-Web is an adaptation of the gRPC protocol for browser-based clients. Native gRPC requires HTTP/2 trailers, which browsers cannot send or access via the Fetch API or XMLHttpRequest. gRPC-Web solves this by encoding gRPC trailers as a special trailing data frame with the MSB set (flag byte 0x80 for uncompressed, 0x81 for compressed). A proxy (Envoy grpc-web filter, an in-process gRPC-Web interceptor, or Connect) translates between gRPC-Web and native gRPC on the server side. The Connect protocol (2022, Buf) is a newer alternative that eliminates the proxy requirement.

gRPC-WebgRPCProtobufBrowserHTTP/1.1Proxy2018

In one line

gRPC-Web adapts gRPC for browsers, which cannot send HTTP/2 trailers. Content-Type: application/grpc-web+proto. Each message is a 5-byte length-prefixed frame: 1-byte flag (0x00 = data, 0x80 = trailer) + 4-byte big-endian length + payload. Trailers are encoded as the last message with flag 0x80 containing key: value text pairs. A proxy (Envoy, Connect) translates to native gRPC/HTTP2. Server streaming works; client streaming and bidirectional streaming require the Connect protocol or browser fetch streams API.

Quick Reference

FieldSizeDescription
Content-Typeapplication/grpc-web+protoBinary Protobuf encoding. Also: application/grpc-web (equivalent), application/grpc-web-text (base64 for CORS environments that cannot pass binary). The +proto suffix is explicit about encoding; omitting it defaults to Protobuf as well.
Frame format5-byte header + payloadEach message is framed: 1 byte flag + 4 bytes big-endian payload length + payload bytes. Flag 0x00 = uncompressed data message. Flag 0x01 = compressed data message. Flag 0x80 = uncompressed trailer message. Flag 0x81 = compressed trailer message. This matches the gRPC/HTTP2 length-prefixed message format.
Trailer encodingFlag byte 0x80gRPC trailers (grpc-status, grpc-message, grpc-status-details-bin) cannot be sent as HTTP/1.1 trailers. gRPC-Web encodes them as the final frame with the MSB set (0x80). The payload is a CRLF-separated sequence of header-like lines: grpc-status: 0\r\ngrpc-message: \r\n. The proxy unwraps this into real HTTP/2 trailers for the gRPC server.
Proxy requirementEnvoy / in-process / nginxgRPC-Web requires a translation proxy between the browser and gRPC server. Options: (1) Envoy grpc_web HTTP filter – production standard, (2) grpc-web in-process proxy (Go/Node.js library wrapping the gRPC server), (3) nginx grpc_pass module with header rewriting, (4) Connect protocol – proxy-free alternative for unary calls.
Streaming supportServer streaming onlyStandard gRPC-Web supports unary (one request, one response) and server streaming (one request, N response frames). Client streaming and bidirectional streaming are not supported in standard gRPC-Web because browsers cannot stream request bodies using standard Fetch. Connect protocol with the Fetch streaming API enables bidirectional streaming in browsers that support it.
application/grpc-web-textBase64 encodingAn alternative content type that base64-encodes all frames. Required in some CORS-constrained environments or CDNs that cannot pass binary response bodies. The base64 overhead is approximately 33% larger payload. Decoded by the client before processing. Not recommended for production unless CORS constraints require it.
grpc-status codes0 = OK, 1–16 = errorsgRPC-Web uses the same 17 status codes as gRPC: 0 OK, 1 CANCELLED, 2 UNKNOWN, 3 INVALID_ARGUMENT, 4 DEADLINE_EXCEEDED, 5 NOT_FOUND, 6 ALREADY_EXISTS, 7 PERMISSION_DENIED, 8 RESOURCE_EXHAUSTED, 12 UNIMPLEMENTED, 13 INTERNAL, 14 UNAVAILABLE, 16 UNAUTHENTICATED. Delivered in the trailer frame.
CORS headersAccess-Control-*Browsers enforce CORS on gRPC-Web requests. The gRPC-Web proxy or server must return Access-Control-Allow-Origin, Access-Control-Allow-Methods: POST, Access-Control-Allow-Headers: content-type,x-grpc-web,x-user-agent, and Access-Control-Expose-Headers: grpc-status,grpc-message. Preflight OPTIONS requests are sent before POST requests.

Key Characteristics

Browser-native gRPC calls

gRPC-Web lets JavaScript and TypeScript front-ends call gRPC services directly using generated client stubs. Type safety from .proto definitions flows to the browser. The grpc-web npm package and protoc-gen-grpc-web plugin generate TypeScript clients from the same .proto files used by backend services.

Proxy translates the protocol gap

The fundamental issue: HTTP/1.1 has no trailer support, and browsers cannot read HTTP/2 trailers via Fetch. The proxy unwraps gRPC-Web frames, reconstitutes HTTP/2 requests with real trailers, and translates responses back. Envoy's grpc_web filter handles this transparently in less than 1 ms overhead per request.

Connect protocol: proxy-free

Connect (Buf, 2022) solves the proxy requirement for unary calls. For unary: standard HTTP POST with Protobuf or JSON body, HTTP status carries gRPC status, no special framing. For streaming: same envelope framing as gRPC-Web but trailers delivered as a JSON object in the final frame. Connect servers accept gRPC, gRPC-Web, and Connect clients without configuration.

No bidirectional streaming without fetch streams

Standard gRPC-Web is limited to unary and server streaming because Fetch streaming request bodies are not universally supported. Full-duplex bidirectional streaming requires the Fetch upload streaming API (Chrome 105+, Safari 17.4+, Firefox partial) plus a Connect server. For broad browser support, bidirectional streaming should be replaced with WebSocket or SSE where possible.

Message Format

Request
http
# gRPC-Web request: unary call (Protobuf)
POST /helloworld.Greeter/SayHello HTTP/1.1
Host: api.example.com
Content-Type: application/grpc-web+proto
X-Grpc-Web: 1
Accept: application/grpc-web+proto

# Body: 5-byte length-prefix frame + Protobuf payload
# Frame header:
#   Byte 0: 0x00  (flag: uncompressed data message)
#   Bytes 1-4: 0x00 0x00 0x00 0x0A  (big-endian payload length = 10)
# Followed by Protobuf-encoded HelloRequest:
#   field 1 (name): "Naveen"
# Full binary (hex): 00 00 00 00 0A 0A 06 4E 61 76 65 65 6E

# gRPC-Web server streaming request:
POST /helloworld.Greeter/SayHelloStream HTTP/1.1
Host: api.example.com
Content-Type: application/grpc-web+proto
X-Grpc-Web: 1

# Same framing as unary – server will return multiple data frames
# followed by one trailer frame (0x80 flag)
Response
http
# gRPC-Web response: unary (success)
HTTP/1.1 200 OK
Content-Type: application/grpc-web+proto
Transfer-Encoding: chunked

# Response body contains TWO frames:

# Frame 1: data message (flag 0x00)
# 00 00 00 00 0E  (flag=0x00, length=14)
# <14 bytes of Protobuf-encoded HelloReply: message="Hello Naveen">

# Frame 2: trailer message (flag 0x80)
# 80 00 00 00 0F  (flag=0x80, length=15)
# grpc-status: 0
  (the trailer key-value text, CRLF-delimited)

# ── Error response (trailer carries non-zero grpc-status): ──────────────
# Frame 1: trailer message (0x80) with error
# 80 00 00 00 2A  (flag=0x80, length=42)
# grpc-status: 5

# grpc-message: method not found

# ── Connect protocol (proxy-free unary): ────────────────────────────────
POST /helloworld.Greeter/SayHello HTTP/1.1
Host: api.example.com
Content-Type: application/proto
Connect-Protocol-Version: 1

# Standard HTTP body (no framing for unary)
# Response:
HTTP/1.1 200 OK
Content-Type: application/proto
# HTTP 200 = gRPC OK. Non-OK returns HTTP 4xx/5xx
# with JSON error body: {"code":"not_found","message":"..."}

Implementations

linuxsince grpc-web npm package (JavaScript/TypeScript). @improbable-eng/grpc-web (Node.js proxy). connect-es (Buf, TypeScript). envoy grpc_web filter. grpc-gateway (Go, HTTP/JSON transcoding).available
macossince Same as Linux. Buf CLI (buf) for code generation. grpcurl for testing gRPC-Web endpoints via Connect.available
windowssince grpc-web npm package. connect-es (TypeScript). Buf CLI binary for Windows.available
iossince gRPC-Web is browser-focused. iOS apps typically use native gRPC-Swift or gRPC-ObjC. Connect-Swift (Buf) supports Connect protocol natively.available
androidsince Android apps use native gRPC-Java or gRPC-Kotlin. Connect-Kotlin (Buf) supports Connect protocol. gRPC-Web applies to browser/WebView contexts.available