Nginx gRPC Reverse Proxy Returning HTTP 502 or HTTP 405 Error
Quick answer
Proxying gRPC traffic through Nginx requires specific configuration distinct from regular HTTP proxying — a 405 typically means Nginx is treating the request...
Proxying gRPC traffic through Nginx requires specific configuration distinct from regular HTTP proxying — a 405 typically means Nginx is treating the request as regular HTTP rather than gRPC, while a 502 typically means the gRPC-aware proxy configuration is correct but the actual connection to the backend gRPC server is failing.
The Problem
Requests to a gRPC service proxied through Nginx fail with one of two distinct errors:
HTTP/1.1 405 Not Allowed
Or:
HTTP/2 502 Bad Gateway
Direct requests to the gRPC backend, bypassing Nginx, work correctly, confirming the issue is specifically in how Nginx is configured to handle the gRPC traffic.
Why It Happens
gRPC uses HTTP/2 with specific framing and content-type requirements that differ meaningfully from typical REST/HTTP APIs, and Nginx needs to be explicitly configured to proxy this correctly rather than treating it like ordinary HTTP traffic:
- A 405 commonly means Nginx is using the standard
proxy_passdirective (meant for regular HTTP) rather than the gRPC-specificgrpc_passdirective, so the request isn't being correctly framed and forwarded as gRPC at all - A 502 commonly means gRPC-aware proxying is correctly configured, but the actual connection between Nginx and the backend gRPC server is failing — often due to a protocol mismatch (plaintext gRPC versus TLS-secured gRPC) between what Nginx is configured to send and what the backend actually expects
- Nginx's
listendirective not enabling HTTP/2, which gRPC fundamentally requires — attempting to proxy gRPC over a connection that isn't actually using HTTP/2 produces broken or rejected requests - Missing or incorrect handling of gRPC-specific trailing headers and streaming semantics, which some older Nginx versions or misconfigured setups don't fully support
The Fix
Use grpc_pass instead of proxy_pass for locations proxying gRPC traffic — this is the single most important configuration difference, and using the wrong directive is the most common root cause of a 405 specifically:
server {
listen 443 ssl http2;
server_name grpc.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
grpc_pass grpc://backend:50051;
}
}
Note both the explicit http2 flag on the listen directive (required for gRPC) and the grpc:// scheme prefix in the grpc_pass target, both of which are necessary parts of correct gRPC proxy configuration.
If your backend gRPC server itself uses TLS (rather than plaintext gRPC internally), use grpcs:// instead of grpc:// to match, since a scheme mismatch between what Nginx attempts and what the backend actually expects produces exactly the kind of 502 connection failure described above:
location / {
grpc_pass grpcs://backend:50051;
}
Confirm your backend gRPC server's actual configuration (plaintext versus TLS) matches what you're specifying in Nginx — a common source of confusion is assuming the backend uses the same TLS configuration as the public-facing Nginx listener, when in fact internal backend-to-Nginx communication is often configured as plaintext gRPC even when the public-facing connection uses TLS.
Verify Nginx itself was built with gRPC module support, since grpc_pass requires this specific module to be compiled in — most modern, standard Nginx distributions include it by default, but custom or minimal builds might not:
nginx -V 2>&1 | grep -o with-http_v2_module
If this doesn't show the module as present, you'll need a build of Nginx that includes HTTP/2 and gRPC support, since no configuration change alone can add this capability to a binary that wasn't compiled with it.
For gRPC-specific timeout handling (relevant particularly for long-lived streaming gRPC connections, which behave differently from typical short-lived HTTP requests), configure the gRPC-specific timeout directives rather than assuming the standard proxy_* timeout settings apply:
location / {
grpc_pass grpc://backend:50051;
grpc_read_timeout 300s;
grpc_send_timeout 300s;
}
Still Not Working?
If grpc_pass is correctly configured and the scheme matches your backend's actual TLS configuration, but you still see connection failures, test the gRPC backend directly using a dedicated gRPC client tool (like grpcurl), bypassing Nginx entirely, to confirm the backend itself is genuinely healthy and reachable independent of anything Nginx-related:
grpcurl -plaintext backend:50051 list
If this direct test also fails, the issue is with the backend gRPC server itself, not your Nginx proxy configuration at all — redirecting your troubleshooting effort toward the backend server's own health and configuration rather than continuing to adjust Nginx settings for a backend that isn't actually functioning correctly in the first place.