{"record":{"id":"af5d997fb911117c","repo":"grpc/grpc-go","slug":"invalid-grpc-request-method-q","errorCode":null,"errorMessage":"invalid gRPC request method %q","messagePattern":"invalid gRPC request method %q","errorType":"http","errorClass":null,"httpStatus":405,"severity":"warning","filePath":"internal/transport/handler_server.go","lineNumber":58,"sourceCode":"\t\"google.golang.org/grpc/internal/grpclog\"\n\t\"google.golang.org/grpc/internal/grpcutil\"\n\t\"google.golang.org/grpc/mem\"\n\t\"google.golang.org/grpc/metadata\"\n\t\"google.golang.org/grpc/peer\"\n\t\"google.golang.org/grpc/stats\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\n// NewServerHandlerTransport returns a ServerTransport handling gRPC from\n// inside an http.Handler, or writes an HTTP error to w and returns an error.\n// It requires that the http Server supports HTTP/2.\nfunc NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats stats.Handler, bufferPool mem.BufferPool) (ServerTransport, error) {\n\tif r.Method != http.MethodPost {\n\t\tw.Header().Set(\"Allow\", http.MethodPost)\n\t\tmsg := fmt.Sprintf(\"invalid gRPC request method %q\", r.Method)\n\t\thttp.Error(w, msg, http.StatusMethodNotAllowed)\n\t\treturn nil, errors.New(msg)\n\t}\n\tcontentType := r.Header.Get(\"Content-Type\")\n\t// TODO: do we assume contentType is lowercase? we did before\n\tcontentSubtype, validContentType := grpcutil.ContentSubtype(contentType)\n\tif !validContentType {\n\t\tmsg := fmt.Sprintf(\"invalid gRPC request content-type %q\", contentType)\n\t\thttp.Error(w, msg, http.StatusUnsupportedMediaType)\n\t\treturn nil, errors.New(msg)\n\t}\n\tif r.ProtoMajor != 2 {\n\t\tmsg := \"gRPC requires HTTP/2\"\n\t\thttp.Error(w, msg, http.StatusHTTPVersionNotSupported)\n\t\treturn nil, errors.New(msg)\n\t}\n\tif _, ok := w.(http.Flusher); !ok {\n\t\tmsg := \"gRPC requires a ResponseWriter supporting http.Flusher\"\n\t\thttp.Error(w, msg, http.StatusInternalServerError)\n\t\treturn nil, errors.New(msg)","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/transport/handler_server.go#L40-L76","documentation":"Returned by NewServerHandlerTransport when the incoming HTTP request method is not POST. The gRPC protocol spec mandates POST for all RPC calls. The transport sets an 'Allow: POST' response header and returns HTTP 405 Method Not Allowed. This path is used when embedding a gRPC server inside a standard net/http handler via grpc.NewServer with a HandlerTransport.","triggerScenarios":"An HTTP request reaches the gRPC handler transport (e.g., via http.HandleFunc wrapping grpc server's ServeHTTP) with a method other than POST—GET, PUT, DELETE, etc. The check at handler_server.go:54-58 fires.","commonSituations":"A browser or curl sending a GET to the gRPC endpoint; a health-check probe using GET; a reverse proxy rewriting methods; HTTP/1.1 client accidentally hitting the gRPC handler; load balancer health checks not using POST.","solutions":["Ensure clients use POST for gRPC calls (standard gRPC clients always do).","If health-checking, use grpc health protocol or configure the health checker to use POST.","Route non-POST requests away from the gRPC handler in your HTTP mux.","Verify reverse proxies and load balancers preserve the POST method."],"exampleFix":"// before: health check sends GET to gRPC endpoint\ncurl http://localhost:8080/myapp.Service/Method\n// after: use POST with proper headers\ncurl -X POST -H \"Content-Type: application/grpc\" \\\n  http://localhost:8080/myapp.Service/Method\n\n// or route non-gRPC requests separately:\nmux.HandleFunc(\"/health\", healthHandler) // GET health check\nmux.Handle(\"/\", grpcServer) // gRPC handler for everything else","handlingStrategy":"validation","validationCode":"// Route non-POST requests away from the gRPC handler.\nmux := http.NewServeMux()\nmux.HandleFunc(\"/health\", healthHandler) // GET health checks\nmux.Handle(\"/\", grpcServer)             // gRPC handler\n// Standard gRPC clients always POST, so this is sufficient.","typeGuard":null,"tryCatchPattern":"// NewServerHandlerTransport returns the error; handle it in your HTTP handler:\nst, err := transport.NewServerHandlerTransport(w, r, stats, pool)\nif err != nil {\n    // it already wrote the HTTP error response\n    return\n}","preventionTips":["Ensure all gRPC clients use POST (standard gRPC clients do).","Route health checks and non-gRPC traffic to separate HTTP handlers.","Verify reverse proxies preserve the POST method.","Log unexpected HTTP methods hitting the gRPC endpoint."],"tags":["transport","http","handler-server","grpc","validation"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}