{"record":{"id":"1f7630f9b960dea8","repo":"caddyserver/caddy","slug":"method-not-allowed-1f7630","errorCode":null,"errorMessage":"method not allowed","messagePattern":"method not allowed","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"caddy.go","lineNumber":165,"sourceCode":"// If the resulting config is the same as the previous, no reload will\n// occur unless forceReload is true. If the config is unchanged and not\n// forcefully reloaded, then errConfigUnchanged is returned. This function\n// is safe for concurrent use.\n// The ifMatchHeader can optionally be given a string of the format:\n//\n//\t\"<path> <hash>\"\n//\n// where <path> is the absolute path in the config and <hash> is the expected hash of\n// the config at that path. If the hash in the ifMatchHeader doesn't match\n// the hash of the config, then an APIError with status 412 will be returned.\nfunc changeConfig(method, path string, input []byte, ifMatchHeader string, forceReload bool) error {\n\tswitch method {\n\tcase http.MethodGet,\n\t\thttp.MethodHead,\n\t\thttp.MethodOptions,\n\t\thttp.MethodConnect,\n\t\thttp.MethodTrace:\n\t\treturn fmt.Errorf(\"method not allowed\")\n\t}\n\n\trawCfgMu.Lock()\n\tdefer rawCfgMu.Unlock()\n\n\tif ifMatchHeader != \"\" {\n\t\t// expect the first and last character to be quotes\n\t\tif len(ifMatchHeader) < 2 || ifMatchHeader[0] != '\"' || ifMatchHeader[len(ifMatchHeader)-1] != '\"' {\n\t\t\treturn APIError{\n\t\t\t\tHTTPStatus: http.StatusBadRequest,\n\t\t\t\tErr:        fmt.Errorf(\"malformed If-Match header; expect quoted string\"),\n\t\t\t}\n\t\t}\n\n\t\t// read out the parts\n\t\tparts := strings.Fields(ifMatchHeader[1 : len(ifMatchHeader)-1])\n\t\tif len(parts) != 2 {\n\t\t\treturn APIError{","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddy.go#L147-L183","documentation":"changeConfig is Caddy's internal entry point for the admin API's config endpoints and only accepts mutating methods. GET, HEAD, OPTIONS, CONNECT, and TRACE are rejected up-front with a plain 'method not allowed' error before any lock is taken. In the HTTP handler this maps to 405; when called from Go code it is a guard against using a read method on a write path.","triggerScenarios":"Calling changeConfig(http.MethodGet, ...) directly from Go code; a custom admin router or plugin that routes a GET/HEAD/OPTIONS/CONNECT/TRACE request into changeConfig instead of unsyncedConfigAccess; scripted curl using a read-only verb against POST/PUT/PATCH-only endpoints.","commonSituations":"Plugin authors wiring admin endpoints and reusing changeConfig for reads; curl -X HEAD/OPTIONS against /config/ paths where the adapter expected GET semantics handled elsewhere; version upgrades that tightened method handling.","solutions":["Use unsyncedConfigAccess (or the standard admin GET handlers) for reads — changeConfig is for POST/PUT/PATCH/DELETE only.","Issue requests with a mutating verb: curl -X POST/PUT/PATCH/DELETE as appropriate for /load, /config/, /id/ endpoints.","If writing a custom admin route, dispatch read verbs to a read handler before calling changeConfig."],"exampleFix":"// before\nerr := changeConfig(http.MethodGet, \"/config/\", body, \"\", false)\n\n// after\ncfg, err := unsyncedConfigAccess(http.MethodGet, \"/config/\", nil, nil)","handlingStrategy":"validation","validationCode":"func isMutating(method string) bool {\n    switch method {\n    case http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete:\n        return true\n    }\n    return false\n}\nif !isMutating(method) {\n    return errors.New(\"use unsyncedConfigAccess for reads\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Route reads through GET handlers / unsyncedConfigAccess, never changeConfig.","Check the documented method list per admin endpoint before scripting it."],"tags":["caddy","admin-api","http","method-not-allowed"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}