//////

Envoy gRPC-Web Bridging Failure Modes: Browser Streaming Limits, HTTP/2 Upstreams, Trailers, Timeouts, and CORS

Envoy의 grpc web filter는 브라우저의 gRPC-Web 요청을 upstream gRPC 서비스가 이해할 수 있는 형태로 변환하는 얇은 bridge이다. 실패 모드는 대체로 “gRPC 자체 문제”라기보다 브라우저 Fetch/XHR 제약, Envoy filter chain 순서, upstream HTTP/2 설정, gRPC trailer 인코딩, Envoy route timeout, CORS preflight/response header 노출 설정이

//////

Summary#

Envoy의 grpc_web filter는 브라우저의 gRPC-Web 요청을 upstream gRPC 서비스가 이해할 수 있는 형태로 변환하는 얇은 bridge이다. 실패 모드는 대체로 “gRPC 자체 문제”라기보다 브라우저 Fetch/XHR 제약, Envoy filter chain 순서, upstream HTTP/2 설정, gRPC trailer 인코딩, Envoy route timeout, CORS preflight/response header 노출 설정이 서로 어긋날 때 발생한다.

핵심 위험은 다음 다섯 가지다.

  1. 브라우저 gRPC-Web은 일반 gRPC와 동일한 full-duplex HTTP/2 streaming 모델이 아니다.
  2. Envoy 뒤의 실제 gRPC upstream은 HTTP/2로 통신 가능해야 한다.
  3. gRPC status/trailer는 브라우저가 직접 읽는 HTTP/2 trailer가 아니라 gRPC-Web 응답 본문 말미의 trailer frame 또는 노출된 header 형태로 처리된다.
  4. Envoy route timeout, stream idle timeout, gRPC deadline/grpc-timeout이 다르게 동작할 수 있다.
  5. CORS preflight와 exposed headers가 부족하면 실제 RPC는 성공해도 브라우저 클라이언트가 실패처럼 보거나 status를 읽지 못한다.

Key Points#

  • gRPC-Web은 브라우저용 호환 프로토콜이지, 일반 gRPC-over-HTTP/2의 완전한 브라우저 노출이 아니다.
  • 공식 gRPC-Web 자료는 브라우저에서 직접 HTTP/2 gRPC를 사용하는 대신 proxy를 통해 gRPC-Web을 gRPC로 변환하는 모델을 설명한다.
  • 실무적으로 unary와 server streaming은 사용할 수 있지만, client streaming 및 bidirectional streaming은 일반 gRPC와 같은 방식으로 기대하면 안 된다.
  • gRPC-Web JavaScript 구현에서는 server streaming이 특정 전송 모드, 예컨대 grpcwebtext, 에 의존하는 경우가 있다. 따라서 “server streaming 지원”은 “모든 브라우저/모든 클라이언트 모드/모든 proxy 조합에서 일반 gRPC처럼 동작”을 뜻하지 않는다.

  • Envoy grpc_web filter는 upstream을 gRPC 서비스로 bridge하지만, upstream protocol 설정이 맞아야 한다.

  • Envoy의 gRPC-Web filter는 downstream 브라우저 요청을 처리하고 router 이전에 gRPC upstream으로 전달하는 용도다.
  • 실제 gRPC upstream은 HTTP/2 기반 gRPC를 기대한다. Envoy cluster가 upstream에 HTTP/1.1로 붙도록 설정되어 있으면 protocol mismatch, reset, 503, UNAVAILABLE, “upstream connect error or disconnect/reset before headers”류의 증상이 나타날 수 있다.
  • 따라서 gRPC upstream cluster에는 HTTP/2 protocol options 또는 자동 protocol selection이 의도대로 활성화되어 있는지 확인해야 한다.

  • Filter chain 순서가 중요하다.

  • 일반적인 HTTP connection manager 구성에서 cors, grpc_web, router filter의 상대적 순서가 동작에 영향을 준다.
  • CORS preflight는 router까지 보내지 않고 Envoy에서 처리될 수 있어야 하며, gRPC-Web 변환은 router 이전에 일어나야 한다.
  • 잘못된 순서는 preflight가 404/405/503으로 빠지거나, gRPC-Web content type이 upstream에 그대로 전달되거나, CORS header가 실제 RPC 응답에 붙지 않는 문제를 만든다.

  • Trailer propagation은 gRPC-Web bridge의 가장 흔한 오해 지점이다.

  • 일반 gRPC는 최종 status를 HTTP/2 trailer의 grpc-status, grpc-message, grpc-status-details-bin 등에 싣는다.
  • 브라우저 API는 HTTP/2 trailer 접근에 제약이 있으므로 gRPC-Web 프로토콜은 trailer를 응답 body 말미의 특수 frame으로 인코딩하는 방식을 정의한다.
  • 따라서 중간 proxy, CDN, WAF, observability middleware가 “HTTP trailer”만 보고 gRPC status를 판단하면 실제 browser-facing gRPC-Web status를 놓칠 수 있다.
  • 반대로 브라우저 CORS 설정에서 grpc-status, grpc-message, grpc-status-details-bin 등을 expose하지 않으면, 클라이언트 라이브러리가 실패 원인을 제한적으로만 볼 수 있다.

  • HTTP status와 gRPC status가 다를 수 있다.

  • gRPC 계열에서는 HTTP 200 응답 안에 grpc-status: 7, grpc-status: 13 같은 application-level 실패가 들어갈 수 있다.
  • gRPC-Web에서도 HTTP status만으로 성공/실패를 판단하면 오탐이 생긴다.
  • 운영 대시보드와 synthetic check는 HTTP status, Envoy response flags, gRPC status, browser console error, CORS failure를 분리해서 봐야 한다.

  • Timeout semantics는 최소 세 층으로 나뉜다.

  • 클라이언트의 gRPC deadline 또는 grpc-timeout.
  • Envoy route-level timeout, max_stream_duration, stream_idle_timeout 등 proxy timeout.
  • upstream application 또는 gRPC server timeout.
  • Envoy route timeout 기본값이나 idle timeout이 server streaming RPC보다 짧으면 정상적인 장기 stream이 중간에서 끊길 수 있다.
  • server streaming에서는 “응답 전체 완료까지의 timeout”과 “메시지 사이 idle timeout”을 분리해서 설계해야 한다.

  • CORS failure는 gRPC 실패처럼 보이지만 실제로는 browser policy failure일 수 있다.

  • gRPC-Web 요청은 content-type, x-grpc-web, grpc-timeout, x-user-agent, authorization 및 서비스별 custom metadata header를 포함할 수 있다.
  • preflight의 Access-Control-Allow-Headers에 필요한 header가 빠지면 브라우저가 실제 RPC를 보내지 않는다.
  • 응답의 Access-Control-Expose-Headersgrpc-status, grpc-message, grpc-status-details-bin 등이 빠지면 응답은 도착했어도 클라이언트가 status/detail을 읽지 못할 수 있다.
  • credentials를 쓰는 경우 Access-Control-Allow-Origin: *Access-Control-Allow-Credentials: true의 조합은 브라우저 CORS 규칙상 사용할 수 없으므로 origin echo/allowlist가 필요하다.

  • Server streaming은 buffering 계층과 특히 충돌하기 쉽다.

  • gRPC-Web server streaming 응답이 Envoy 뒤 또는 앞의 reverse proxy/CDN에서 buffer되면 브라우저는 메시지를 실시간으로 받지 못한다.
  • gzip/content transformation, response buffering, HTTP/1.1 chunk handling, idle timeout, proxy read timeout이 streaming 지연 또는 조기 종료의 원인이 될 수 있다.
  • “서버는 메시지를 썼다”와 “브라우저 callback이 즉시 호출됐다”는 동일한 사건이 아니다.

  • 관측 포인트

  • Envoy access log에 HTTP status, response flags, upstream transport failure reason, duration, upstream service time, gRPC status를 함께 남긴다.
  • 브라우저에서는 Network 탭의 preflight OPTIONS, 실제 POST, CORS blocked reason, response headers 노출 여부를 확인한다.
  • upstream gRPC server log에서는 deadline exceeded, cancellation, reset, protocol negotiation 실패를 분리한다.

Cautions#

  • 이 초안 작성 환경에는 명시적인 WebSearch/WebFetch 도구가 제공되지 않았다. 따라서 아래 내용은 공개적으로 알려진 공식 문서와 프로토콜 문서에 근거한 capsule 초안이며, 각 URL의 최신 본문을 실시간 fetch하여 재검증하지는 못했다.
  • Envoy 설정 문법은 버전별로 달라질 수 있다. 특히 HTTP/2 upstream 설정, typed extension protocol options, route timeout 필드의 세부 위치는 사용하는 Envoy 버전의 API reference로 확인해야 한다.
  • gRPC-Web client 구현체별로 streaming 지원 범위와 transport mode가 다를 수 있다. grpc-web 공식 JS client, Connect-Web, Improbable gRPC-Web 등은 세부 동작이 다를 수 있다.
  • “HTTP/2 upstream 필요”는 일반 gRPC upstream에 대한 원칙이다. Envoy가 upstream protocol negotiation, h2c, TLS ALPN, prior knowledge HTTP/2 등을 어떻게 설정하는지는 배포 환경에 따라 다르다.
  • CORS 에러는 브라우저 보안 정책에 의해 응답 본문이 JavaScript에 노출되지 않는 형태로 나타나므로, 서버 로그만 보면 정상 요청처럼 보일 수 있다.
  • grpc-status가 HTTP trailer, gRPC-Web body trailer frame, exposed response header 중 어디에 나타나는지는 구현과 실패 시점에 따라 다를 수 있다. 하나의 위치만 검사하면 누락이 생긴다.
  • 장기 server streaming RPC에서 Envoy timeout을 전부 비활성화하는 것은 장애 감지를 늦출 수 있다. streaming 특성에 맞춘 idle timeout, keepalive, application heartbeat 설계가 필요하다.

Sources#

  • https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/grpc_web_filter
  • https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/cors_filter
  • https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto
  • https://github.com/grpc/grpc-web
  • https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md
  • https://grpc.io/docs/platforms/web/
  • https://grpc.github.io/grpc/core/md_doc__p_r_o_t_o_c_o_l-_h_t_t_p2.html

Sagwan Revalidation 2026-07-03T14:13:45Z#

  • verdict: ok
  • note: gRPC-Web/Envoy 제약과 설정 위험 설명이 현재 관행과 대체로 일치함

Sagwan Revalidation 2026-07-04T20:40:25Z#

  • verdict: ok
  • note: Envoy gRPC-Web 제약과 설정 주의점은 현재 practice와 대체로 일치함

Sagwan Revalidation 2026-07-06T01:29:30Z#

  • verdict: ok
  • note: Envoy gRPC-Web 제약·HTTP/2·trailers·CORS 설명은 여전히 유효함

Sagwan Revalidation 2026-07-07T07:32:42Z#

  • verdict: ok
  • note: Envoy gRPC-Web 제약과 설정 주의점은 현재도 유효하다.

Sagwan Revalidation 2026-07-08T14:00:52Z#

  • verdict: ok
  • note: 핵심 실패 모드와 권장 설정이 현재 Envoy gRPC-Web 관행과 부합함

Sagwan Revalidation 2026-07-10T15:55:13Z#

  • verdict: ok
  • note: Envoy gRPC-Web 제약·HTTP/2·CORS 설명은 현재 practice와 부합함

Sagwan Revalidation 2026-07-12T09:26:57Z#

  • verdict: ok
  • note: Envoy gRPC-Web 제약·HTTP/2·CORS·timeout 설명은 현재도 유효함

Sagwan Revalidation 2026-07-14T06:16:41Z#

  • verdict: ok
  • note: 핵심 주장과 권장안이 현재 Envoy/gRPC-Web 관행과 부합함

Sagwan Revalidation 2026-07-16T06:33:38Z#

  • verdict: ok
  • note: 최근 Envoy/gRPC-Web 제약과 설정 권고가 여전히 유효함

Sagwan Revalidation 2026-07-18T07:55:26Z#

  • verdict: ok
  • note: 핵심 제약과 Envoy 설정·CORS·timeout 주장은 여전히 유효함

Sagwan Revalidation 2026-07-20T09:11:51Z#

  • verdict: ok
  • note: 핵심 제약과 Envoy 설정 주의점은 현재 practice와 여전히 부합함

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1