<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/grpc-web-failed-the-web-buf-yxi6aoslo" -->

---
title: gRPC-Web Failed the Web · Buf | daily.dev
description: gRPC-Web was created to bring gRPC&#x27;s schema-first, type-safe model into browsers, but it buries RPC failures inside the response body since browsers can&#x27;t read...
canonical: https://daily.dev/posts/grpc-web-failed-the-web-buf-yxi6aoslo
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: gRPC-Web Failed the Web · Buf | daily.dev
og:description: gRPC-Web was created to bring gRPC&#x27;s schema-first, type-safe model into browsers, but it buries RPC failures inside the response body since browsers can&#x27;t read...
og:url: https://daily.dev/posts/grpc-web-failed-the-web-buf-yxi6aoslo
og:image: https://api.daily.dev/og/posts/YxI6aOslo.png
og:image:alt: gRPC-Web Failed the Web · Buf
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# gRPC-Web Failed the Web · Buf

**[Buf Blog](https://daily.dev/sources/buf)** · 11 min read · 50 upvotes · 8 comments

## Summary

gRPC-Web was created to bring gRPC's schema-first, type-safe model into browsers, but it buries RPC failures inside the response body since browsers can't read HTTP trailers, hiding real errors from CDNs, proxies, and monitoring that only see a 200 OK. The grpc/grpc-web project is now in maintenance mode, and its recommended alternative, gRPC-Gateway, breaks the direct Protobuf contract by introducing a JSON/HTTP translation layer. Connect, built by Buf, is presented as an alternative protocol that keeps the Protobuf contract and generated clients while using standard HTTP status codes, unwrapped JSON/proto bodies, and optional GET caching, letting browsers talk directly to a ConnectRPC server without a translating proxy like Envoy.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://buf.build/blog/grpc-web-failed-the-web>

## Questions this post answers

### Why does gRPC-Web return a 200 OK HTTP status even when the RPC itself fails?

gRPC and gRPC-Web report the RPC outcome through a separate grpc-status field rather than the HTTP status code, so the transport-level response is 200 OK even when the call fails internally. In gRPC-Web this status is moved into the response body as a trailer frame because browsers cannot read HTTP trailers, meaning CDNs, proxies, and monitoring tools that only understand HTTP see 100% success while every RPC call actually fails.

_Debugging misleading success metrics in gRPC-Web setups gets easier with clear technical writeups surfaced on daily.dev._

### What does Google's grpc/grpc-web project recommend now that it's in maintenance mode?

The grpc-web roadmap states no new features are planned, citing the archival of Google Closure and minimal maintenance of Protobuf JavaScript, and instead recommends gRPC-Gateway. However, gRPC-Gateway doesn't implement gRPC-Web at all; it transcodes gRPC into a separate JSON REST API, which moves where the Protobuf contract ends and requires generating OpenAPI and a second client to keep type safety in the browser.

_Teams weighing gRPC-Web alternatives can track ecosystem shifts like this on daily.dev._

### How does the Connect protocol differ from gRPC-Web in handling errors and unary request bodies?

Connect uses standard HTTP status codes for unary call failures instead of burying them in the response body, so a database outage returns an HTTP 500 that CDNs, dashboards, and clients all understand natively. Unary bodies are just the serialized message with Content-Type application/json or application/proto, with no five-byte length prefix or trailer frame, unlike gRPC-Web which wraps even single-response calls in streaming-style framing.

_Choosing between gRPC-Web and Connect for browser RPC gets simpler with concrete comparisons like this on daily.dev._

## Community discussion

Top comments from developers on daily.dev.

**@ahmetozel** · 3 upvotes

> Errors hidden behind a 200 is the kind of design decision whose cost is entirely operational and therefore invisible until you are on call. Every alert you would normally get for free stops working: error rate dashboards read zero, retry logic in proxies never triggers, and the CDN happily caches a failure. You end up rebuilding all of that at the application layer, per client, and only for this one protocol. Using real status codes is unglamorous but it is what lets the rest of the stack keep doing its job, which is usually the stronger argument than the type-safety one.

**@agustinbarrientos** · 2 upvotes

> How reliably does Connect's end-stream status survive proxies after a server stream has already committed 200? I'd test that path through a CDN and reverse proxy before treating streaming failures like normal HTTP errors.

**@gtxpro1** · 0 upvotes

> ![GIF](https://static.klipy.com/ii/d7aec6f6f171607374b2065c836f92f4/83/a0/GPI2QgDt.gif)

---

Tags: [#architecture](https://daily.dev/tags/architecture), [#backend](https://daily.dev/tags/backend), [#grpc](https://daily.dev/tags/grpc)

[View this post on daily.dev](https://daily.dev/posts/grpc-web-failed-the-web-buf-yxi6aoslo)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"gRPC-Web Failed the Web · Buf","url":"https://daily.dev/posts/grpc-web-failed-the-web-buf-yxi6aoslo","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/grpc-web-failed-the-web-buf-yxi6aoslo"},"datePublished":"2026-09-02T12:08:55.766Z","dateModified":"2026-09-02T12:37:58.626Z","description":"gRPC-Web was created to bring gRPC's schema-first, type-safe model into browsers, but it buries RPC failures inside the response body since browsers can't read...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/e5d0a084569950896daf76a5c2ac6791?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/e5d0a084569950896daf76a5c2ac6791?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Buf Blog","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Buf Blog","logo":"https://media.daily.dev/image/upload/s--2YBgo_Nm--/f_auto,q_auto/v1787469605/logos/buf?_a=BAMAMicg0","url":"https://daily.dev/sources/buf"},"commentCount":8,"discussionUrl":"https://daily.dev/posts/grpc-web-failed-the-web-buf-yxi6aoslo","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":50},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":8}],"keywords":"architecture,backend,grpc","timeRequired":"PT11M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Buf Blog","item":"https://daily.dev/sources/buf"},{"@type":"ListItem","position":3,"name":"gRPC-Web Failed the Web · Buf"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/grpc-web-failed-the-web-buf-yxi6aoslo","comment":[{"@type":"Comment","text":"Errors hidden behind a 200 is the kind of design decision whose cost is entirely operational and therefore invisible until you are on call. Every alert you would normally get for free stops working: error rate dashboards read zero, retry logic in proxies never triggers, and the CDN happily caches a failure. You end up rebuilding all of that at the application layer, per client, and only for this one protocol. Using real status codes is unglamorous but it is what lets the rest of the stack keep doing its job, which is usually the stronger argument than the type-safety one.","datePublished":"2026-09-05T10:09:25.732Z","url":"https://daily.dev/posts/YxI6aOslo#c-kDXDuLRHw","author":{"@type":"Person","name":"Ahmet Özel","url":"https://daily.dev/ahmetozel","image":"https://avatars.githubusercontent.com/u/70992231?v=4"},"interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":3}},{"@type":"Comment","text":"How reliably does Connect’s end-stream status survive proxies after a server stream has already committed 200? I’d test that path through a CDN and reverse proxy before treating streaming failures like normal HTTP errors.","datePublished":"2026-09-03T21:57:25.622Z","url":"https://daily.dev/posts/YxI6aOslo#c-Sm505veW2","author":{"@type":"Person","name":"Agustin Barrientos","url":"https://daily.dev/agustinbarrientos","image":"https://media.daily.dev/image/upload/s--5ayxQnqn--/f_auto/v1788281802/avatars/avatar_wQYYVe5Tbj0NJ7C7qPoa8?_a=BAMAMicg0"},"interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":2}},{"@type":"Comment","text":"","datePublished":"2026-09-07T09:22:41.178Z","url":"https://daily.dev/posts/YxI6aOslo#c-80r9MpgqZ","author":{"@type":"Person","name":"NguyenDat","url":"https://daily.dev/gtxpro1","image":"https://lh3.googleusercontent.com/a-/AOh14Gj6dJn9PAc86GicePx7UzdLxzXxJXuU3IjYkyK4=s100"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/grpc-web-failed-the-web-buf-yxi6aoslo#faq","mainEntity":[{"@type":"Question","name":"Why does gRPC-Web return a 200 OK HTTP status even when the RPC itself fails?","acceptedAnswer":{"@type":"Answer","text":"gRPC and gRPC-Web report the RPC outcome through a separate grpc-status field rather than the HTTP status code, so the transport-level response is 200 OK even when the call fails internally. In gRPC-Web this status is moved into the response body as a trailer frame because browsers cannot read HTTP trailers, meaning CDNs, proxies, and monitoring tools that only understand HTTP see 100% success while every RPC call actually fails. Debugging misleading success metrics in gRPC-Web setups gets easier with clear technical writeups surfaced on daily.dev."}},{"@type":"Question","name":"What does Google's grpc/grpc-web project recommend now that it's in maintenance mode?","acceptedAnswer":{"@type":"Answer","text":"The grpc-web roadmap states no new features are planned, citing the archival of Google Closure and minimal maintenance of Protobuf JavaScript, and instead recommends gRPC-Gateway. However, gRPC-Gateway doesn't implement gRPC-Web at all; it transcodes gRPC into a separate JSON REST API, which moves where the Protobuf contract ends and requires generating OpenAPI and a second client to keep type safety in the browser. Teams weighing gRPC-Web alternatives can track ecosystem shifts like this on daily.dev."}},{"@type":"Question","name":"How does the Connect protocol differ from gRPC-Web in handling errors and unary request bodies?","acceptedAnswer":{"@type":"Answer","text":"Connect uses standard HTTP status codes for unary call failures instead of burying them in the response body, so a database outage returns an HTTP 500 that CDNs, dashboards, and clients all understand natively. Unary bodies are just the serialized message with Content-Type application/json or application/proto, with no five-byte length prefix or trailer frame, unlike gRPC-Web which wraps even single-response calls in streaming-style framing. Choosing between gRPC-Web and Connect for browser RPC gets simpler with concrete comparisons like this on daily.dev."}}]}
```

