This is a simple gRPC communication between a web client and server with an Envoy proxy. I’m using the gRPC-Web library for this. gRPC-Web is a JavaScript client library that allows web applications to talk to gRPC services. Since browsers don’t support HTTP/2 or the binary protocol used by standard gRPC, gRPC-Web provides a way to bridge the gap by using HTTP/1.1 or HTTP/2 and encoding gRPC messages in a way that browsers can handle. Here’s how gRPC-Web works: 1- The client sends requests to the server using gRPC-Web, which typically uses HTTP/1.1 or HTTP/2. Metadata (like headers) can be attached to the request, such as for authentication (e.g., JWT tokens). The request is encoded in gRPC-Web format, typically using base64 encoding for the binary gRPC payload. The client sends this request over HTTP/1.1 or HTTP/2. 2- Envoy (or another reverse proxy like Nginx) sits between the gRPC-Web client and the gRPC server. Envoy receives the gRPC-Web request, decodes the gRPC-Web payload, and forwards it as a standard gRPC request to the gRPC server using HTTP/2. The gRPC server processes the request as if it were a native gRPC request, using HTTP/2. 3- The gRPC server processes the incoming gRPC request, does the business logic, and generates a response (in this case, the Go written application). The response is encoded in the standard gRPC format and sent back to Envoy. 4- Envoy receives the gRPC response, encodes it in gRPC-Web format (typically base64), and sends it back to the gRPC-Web client over HTTP/1.1 or HTTP/2. Any metadata in the gRPC response, such as status codes, is translated. 5- The gRPC-Web client decodes the response and uses the web application. NOTE: Client-side and Bi-directional streaming is not currently supported (see streaming roadmap) Advantages of gRPC-Web Browser Compatibility: Allows modern web applications to interact with gRPC services without needing native support for HTTP/2 and binary protocols. Efficiency: Leverages the performance and efficiency of gRPC while adapting it for the web. Here Is a Working Demo https://github.com/ehsaniara/gRPC-web-example Demo Quick Start docker compose up -d Then, open the browser on__http://localhost:8081__ (note: this demo uses 8081 and 8080 ports) Project Structure client/: Contains the gRPC-Web client code server/: Contains the gRPC server code written in Go envoy/: envoy configuration file Prerequisites Go Node.js and npm grpc-web protoc compiler Proto File syntax = "proto3"; package helloworld; option go_package = "./proto"; // Add this line service Greeter { rpc SayHello (HelloRequest) returns (HelloReply); } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } Server Server’s main file (server/main.go): package main import ( "context" "google.golang.org/grpc/reflection" "log" "net" pb "github.com/ehsaniara/gRPC-web-example/proto" "google.golang.org/grpc" ) type server struct { pb.UnimplementedGreeterServer } func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: "Hello " + in.Name}, nil } func main() { lis, err := net.Listen("tcp", ":50051") if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() pb.RegisterGreeterServer(s, &server{}) // Register reflection service on gRPC server. reflection.Register(s) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } log.Println("Server is running on port 50051") if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } } Envoy Config ... http_filters: - name: envoy.filters.http.grpc_web typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb - name: envoy.filters.http.cors typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router ... Web Client Client file (client/src/index.js ) // Import the generated gRPC-Web client stubs and message classes import {GreeterClient} from './generated/helloworld_grpc_web_pb'; import {HelloRequest} from './generated/helloworld_pb'; // Create an instance of the Greeter client const client = new GreeterClient('http://localhost:8080'); // Function to send a greeting request function sayHello(name) { // Create a new request const request = new HelloRequest(); request.setName(name); // Call the sayHello method on the Greeter client client.sayHello(request, {}, (err, response) => { if (err) { console.error('Error:', err.message); document.getElementById('output').textContent = 'Error: ' + err.message; } else { console.log('Greeting:', response.getMessage()); document.getElementById('output').textContent = 'Greeting: ' + response.getMessage(); } }); } // Example usage: sending a request when the page loads document.addEventListener('DOMContentLoaded', () => { const name = 'World'; sayHello(name); }); Run generate.sh to generate *_pb.js files, for which you need to have grpc-web and protoc-gen-js already installed in your machine. $ npm i grpc-web $ npm install -g protoc-gen-js This will generate helloworld_grpc_web_pb.js and helloworld_pb.js files, which already exist in this demo. Here’s a related GitHub project: https://github.com/ehsaniara/gRPC-web-example?embedable=true This is a simple gRPC communication between a web client and server with an Envoy proxy. I’m using the gRPC-Web library for this. gRPC-Web is a JavaScript client library that allows web applications to talk to gRPC services. Since browsers don’t support HTTP/2 or the binary protocol used by standard gRPC, gRPC-Web provides a way to bridge the gap by using HTTP/1.1 or HTTP/2 and encoding gRPC messages in a way that browsers can handle. Here’s how gRPC-Web works: 1- The client sends requests to the server using gRPC-Web, which typically uses HTTP/1.1 or HTTP/2. Metadata (like headers) can be attached to the request, such as for authentication (e.g., JWT tokens). The request is encoded in gRPC-Web format, typically using base64 encoding for the binary gRPC payload. The client sends this request over HTTP/1.1 or HTTP/2. The request is encoded in gRPC-Web format, typically using base64 encoding for the binary gRPC payload. The client sends this request over HTTP/1.1 or HTTP/2. The request is encoded in gRPC-Web format, typically using base64 encoding for the binary gRPC payload. The client sends this request over HTTP/1.1 or HTTP/2. 2- Envoy (or another reverse proxy like Nginx) sits between the gRPC-Web client and the gRPC server. Envoy receives the gRPC-Web request, decodes the gRPC-Web payload, and forwards it as a standard gRPC request to the gRPC server using HTTP/2. The gRPC server processes the request as if it were a native gRPC request, using HTTP/2. The gRPC server processes the request as if it were a native gRPC request, using HTTP/2. The gRPC server processes the request as if it were a native gRPC request, using HTTP/2. 3- The gRPC server processes the incoming gRPC request, does the business logic, and generates a response (in this case, the Go written application). The response is encoded in the standard gRPC format and sent back to Envoy. 4- Envoy receives the gRPC response, encodes it in gRPC-Web format (typically base64), and sends it back to the gRPC-Web client over HTTP/1.1 or HTTP/2. Any metadata in the gRPC response, such as status codes, is translated. 5- The gRPC-Web client decodes the response and uses the web application. NOTE: Client-side and Bi-directional streaming is not currently supported (see streaming roadmap) NOTE: Client-side and Bi-directional streaming is not currently supported (see streaming roadmap ) streaming roadmap streaming roadmap Advantages of gRPC-Web Browser Compatibility: Allows modern web applications to interact with gRPC services without needing native support for HTTP/2 and binary protocols. Browser Compatibility : Allows modern web applications to interact with gRPC services without needing native support for HTTP/2 and binary protocols. Browser Compatibility Efficiency: Leverages the performance and efficiency of gRPC while adapting it for the web. Efficiency : Leverages the performance and efficiency of gRPC while adapting it for the web. Efficiency Here Is a Working Demo https://github.com/ehsaniara/gRPC-web-example https://github.com/ehsaniara/gRPC-web-example Demo Quick Start docker compose up -d docker compose up -d Then, open the browser on__ http://localhost:8081 __ (note: this demo uses 8081 and 8080 ports) http://localhost:8081 Project Structure client/: Contains the gRPC-Web client code server/: Contains the gRPC server code written in Go envoy/: envoy configuration file client/: Contains the gRPC-Web client code server/: Contains the gRPC server code written in Go envoy/: envoy configuration file Prerequisites Go Node.js and npm grpc-web protoc compiler Go Go Go Node.js and npm Node.js and npm Node.js and npm grpc-web grpc-web grpc-web protoc compiler protoc compiler protoc compiler Proto File syntax = "proto3"; package helloworld; option go_package = "./proto"; // Add this line service Greeter { rpc SayHello (HelloRequest) returns (HelloReply); } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } syntax = "proto3"; package helloworld; option go_package = "./proto"; // Add this line service Greeter { rpc SayHello (HelloRequest) returns (HelloReply); } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } Server Server’s main file ( server/main.go ): server/main.go package main import ( "context" "google.golang.org/grpc/reflection" "log" "net" pb "github.com/ehsaniara/gRPC-web-example/proto" "google.golang.org/grpc" ) type server struct { pb.UnimplementedGreeterServer } func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: "Hello " + in.Name}, nil } func main() { lis, err := net.Listen("tcp", ":50051") if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() pb.RegisterGreeterServer(s, &server{}) // Register reflection service on gRPC server. reflection.Register(s) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } log.Println("Server is running on port 50051") if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } } package main import ( "context" "google.golang.org/grpc/reflection" "log" "net" pb "github.com/ehsaniara/gRPC-web-example/proto" "google.golang.org/grpc" ) type server struct { pb.UnimplementedGreeterServer } func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: "Hello " + in.Name}, nil } func main() { lis, err := net.Listen("tcp", ":50051") if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() pb.RegisterGreeterServer(s, &server{}) // Register reflection service on gRPC server. reflection.Register(s) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } log.Println("Server is running on port 50051") if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } } Envoy Config ... http_filters: - name: envoy.filters.http.grpc_web typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb - name: envoy.filters.http.cors typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router ... ... http_filters: - name: envoy.filters.http.grpc_web typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb - name: envoy.filters.http.cors typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router ... Web Client Client file ( client/src/index.js ) client/src/index.js // Import the generated gRPC-Web client stubs and message classes import {GreeterClient} from './generated/helloworld_grpc_web_pb'; import {HelloRequest} from './generated/helloworld_pb'; // Create an instance of the Greeter client const client = new GreeterClient('http://localhost:8080'); // Function to send a greeting request function sayHello(name) { // Create a new request const request = new HelloRequest(); request.setName(name); // Call the sayHello method on the Greeter client client.sayHello(request, {}, (err, response) => { if (err) { console.error('Error:', err.message); document.getElementById('output').textContent = 'Error: ' + err.message; } else { console.log('Greeting:', response.getMessage()); document.getElementById('output').textContent = 'Greeting: ' + response.getMessage(); } }); } // Example usage: sending a request when the page loads document.addEventListener('DOMContentLoaded', () => { const name = 'World'; sayHello(name); }); // Import the generated gRPC-Web client stubs and message classes import {GreeterClient} from './generated/helloworld_grpc_web_pb'; import {HelloRequest} from './generated/helloworld_pb'; // Create an instance of the Greeter client const client = new GreeterClient('http://localhost:8080'); // Function to send a greeting request function sayHello(name) { // Create a new request const request = new HelloRequest(); request.setName(name); // Call the sayHello method on the Greeter client client.sayHello(request, {}, (err, response) => { if (err) { console.error('Error:', err.message); document.getElementById('output').textContent = 'Error: ' + err.message; } else { console.log('Greeting:', response.getMessage()); document.getElementById('output').textContent = 'Greeting: ' + response.getMessage(); } }); } // Example usage: sending a request when the page loads document.addEventListener('DOMContentLoaded', () => { const name = 'World'; sayHello(name); }); Run generate.sh to generate *_pb.js files, for which you need to have grpc-web and protoc-gen-js already installed in your machine. generate.sh generate.sh *_pb.js grpc-web protoc-gen-js $ npm i grpc-web $ npm install -g protoc-gen-js $ npm i grpc-web $ npm install -g protoc-gen-js This will generate helloworld_grpc_web_pb.js and helloworld_pb.js files, which already exist in this demo. helloworld_grpc_web_pb.js helloworld_pb.js Here’s a related GitHub project: https://github.com/ehsaniara/gRPC-web-example?embedable=true https://github.com/ehsaniara/gRPC-web-example?embedable=true