grpc/grpc-go · error
gRPC requires HTTP/2
Error message
gRPC requires HTTP/2
What it means
Returned by NewServerHandlerTransport when r.ProtoMajor is not 2. gRPC is built on HTTP/2 and cannot function over HTTP/1.1 because it relies on HTTP/2 multiplexing, streaming, and trailers. The transport rejects the request with HTTP 505 HTTP Version Not Supported. This only affects the handler-server (net/http integration) transport path.
Solutions
- Enable HTTP/2 on your net/http server: use TLS with ALPN ('h2') via http.ServeTLS, or enable h2c for cleartext via golang.org/x/net/http2/h2c.
- Ensure the TLS certificate and server config advertise h2 in NextProtos.
- For cleartext (no TLS), wrap the handler with h2c.NewHandler.
- Verify clients and proxies are HTTP/2 capable.
Example fix
// before: HTTP/1.1 only
http.ListenAndServe(":8080", grpcHandler)
// after: enable h2c for cleartext HTTP/2
import "golang.org/x/net/http2/h2c"
h2s := &http.Server{Handler: h2c.NewHandler(grpcHandler, &http2.Server{})}
h2s.ListenAndServe()
// or with TLS + ALPN:
http.ListenAndServeTLS(":443", "cert.pem", "key.pem", grpcHandler) Defensive patterns
Strategy: validation
Validate before calling
// Ensure the HTTP server supports HTTP/2 before serving gRPC.
// With TLS: use http.ServeTLS with ALPN h2.
// Without TLS: use h2c.NewHandler.
import "golang.org/x/net/http2/h2c"
h2s := &http.Server{
Handler: h2c.NewHandler(grpcServer, &http2.Server{}),
}
h2s.ListenAndServe() Try / catch
st, err := transport.NewServerHandlerTransport(w, r, stats, pool)
if err != nil {
if strings.Contains(err.Error(), "requires HTTP/2") {
log.Fatal("server must use HTTP/2; enable h2c or TLS+ALPN")
}
} Prevention
- Enable HTTP/2 on your net/http server (TLS+ALPN for h2, or h2c for cleartext).
- Test with HTTP/2-capable clients (standard gRPC clients use HTTP/2).
- Verify load balancers and proxies support and negotiate HTTP/2.
- Do not use plain http.ListenAndServe for gRPC handler transports.
When it happens
Trigger: An HTTP/1.1 request reaches the gRPC handler transport. This happens when the net/http server is not configured for HTTP/2 (no TLS, or TLS without h2 ALPN, or h2c not enabled for cleartext), so the request arrives as HTTP/1.1.
Common situations: Running gRPC handler on plain HTTP without h2c; TLS server without ALPN negotiation for h2; using http.ListenAndServe (HTTP/1.1 only) instead of http2-compatible setup; client connecting via HTTP/1.1 proxy that doesn't upgrade.
Related errors
- ErrCodeEnhanceYourCalm
- failed to write the HTTP request
- gRPC requires a ResponseWriter supporting http.Flusher
- invalid gRPC request content-type
- invalid gRPC request method
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/45655b4eba47ac19.
Report an issue: GitHub.
Appendix: source
Thrown at internal/transport/handler_server.go:71
func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats stats.Handler, bufferPool mem.BufferPool) (ServerTransport, error) {
if r.Method != http.MethodPost {
w.Header().Set("Allow", http.MethodPost)
msg := fmt.Sprintf("invalid gRPC request method %q", r.Method)
http.Error(w, msg, http.StatusMethodNotAllowed)
return nil, errors.New(msg)
}
contentType := r.Header.Get("Content-Type")
// TODO: do we assume contentType is lowercase? we did before
contentSubtype, validContentType := grpcutil.ContentSubtype(contentType)
if !validContentType {
msg := fmt.Sprintf("invalid gRPC request content-type %q", contentType)
http.Error(w, msg, http.StatusUnsupportedMediaType)
return nil, errors.New(msg)
}
if r.ProtoMajor != 2 {
msg := "gRPC requires HTTP/2"
http.Error(w, msg, http.StatusHTTPVersionNotSupported)
return nil, errors.New(msg)
}
if _, ok := w.(http.Flusher); !ok {
msg := "gRPC requires a ResponseWriter supporting http.Flusher"
http.Error(w, msg, http.StatusInternalServerError)
return nil, errors.New(msg)
}
var localAddr net.Addr
if la := r.Context().Value(http.LocalAddrContextKey); la != nil {
localAddr, _ = la.(net.Addr)
}
var authInfo credentials.AuthInfo
if r.TLS != nil {
authInfo = credentials.TLSInfo{State: *r.TLS, CommonAuthInfo: credentials.CommonAuthInfo{SecurityLevel: credentials.PrivacyAndIntegrity}}
}
p := peer.Peer{
Addr: strAddr(r.RemoteAddr),
LocalAddr: localAddr,View on GitHub (pinned to 0c51461d27)