{"record":{"id":"6d0fe7bb8b3328dd","repo":"caddyserver/caddy","slug":"method-not-allowed-6d0fe7","errorCode":null,"errorMessage":"method not allowed","messagePattern":"method not allowed","errorType":"http","errorClass":"caddy.APIError","httpStatus":405,"severity":"warning","filePath":"modules/caddyhttp/reverseproxy/admin.go","lineNumber":66,"sourceCode":"}\n\n// Routes returns a route for the /reverse_proxy/upstreams endpoint.\nfunc (al adminUpstreams) Routes() []caddy.AdminRoute {\n\treturn []caddy.AdminRoute{\n\t\t{\n\t\t\tPattern: \"/reverse_proxy/upstreams\",\n\t\t\tHandler: caddy.AdminHandlerFunc(al.handleUpstreams),\n\t\t},\n\t}\n}\n\n// handleUpstreams reports the status of the reverse proxy\n// upstream pool.\nfunc (adminUpstreams) handleUpstreams(w http.ResponseWriter, r *http.Request) error {\n\tif r.Method != http.MethodGet {\n\t\treturn caddy.APIError{\n\t\t\tHTTPStatus: http.StatusMethodNotAllowed,\n\t\t\tErr:        fmt.Errorf(\"method not allowed\"),\n\t\t}\n\t}\n\n\t// Prep for a JSON response\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tenc := json.NewEncoder(w)\n\n\t// Collect the results to respond with\n\tresults := []upstreamStatus{}\n\tknownHosts := make(map[string]struct{})\n\n\t// Iterate over the static upstream pool (needs to be fast)\n\tvar rangeErr error\n\thosts.Range(func(key, val any) bool {\n\t\taddress, ok := key.(string)\n\t\tif !ok {\n\t\t\trangeErr = caddy.APIError{\n\t\t\t\tHTTPStatus: http.StatusInternalServerError,","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/reverseproxy/admin.go#L48-L84","documentation":"The admin API endpoint GET /reverse_proxy/upstreams (which reports the state of the reverse proxy upstream pool) rejects any HTTP method other than GET with 405 Method Not Allowed, wrapped in a caddy.APIError.","triggerScenarios":"Sending POST, PUT, DELETE, etc. to the admin endpoint on the admin listener, e.g. 'curl -X POST localhost:2019/reverse_proxy/upstreams'.","commonSituations":"Monitoring scripts that default to POST with a JSON body; curl invocations with -X flags copied from write-style admin endpoints like /load.","solutions":["Use a plain GET request: 'curl localhost:2019/reverse_proxy/upstreams'.","Remove -X POST/-d flags from monitoring/health-check commands hitting read-only admin endpoints.","Treat a 405 from this endpoint as a client bug, not a server problem."],"exampleFix":"# before\ncurl -X POST http://localhost:2019/reverse_proxy/upstreams -d '{}'\n\n# after\ncurl http://localhost:2019/reverse_proxy/upstreams","handlingStrategy":"validation","validationCode":"// client side: never send a body or non-GET method to this endpoint\nreq, _ := http.NewRequest(http.MethodGet, adminURL+\"/reverse_proxy/upstreams\", nil)\n// server side (if wrapping admin endpoints):\nif r.Method != http.MethodGet {\n    w.Header().Set(\"Allow\", http.MethodGet)\n    http.Error(w, \"method not allowed\", http.StatusMethodNotAllowed)\n}","typeGuard":null,"tryCatchPattern":"resp, err := http.Get(url)\nif err == nil && resp.StatusCode == http.StatusMethodNotAllowed {\n    // fix the caller: this endpoint is read-only GET\n    log.Print(\"/reverse_proxy/upstreams is GET-only; remove -X/-d from the request\")\n}","preventionTips":["Treat admin read endpoints as GET-only by convention.","Review curl invocations before adding -X or -d flags.","Check the Allow header on 405s to learn the accepted method."],"tags":["admin-api","reverse-proxy","http-method","monitoring"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}