caddyserver/caddy · warning · caddy.APIError

method not allowed

Error message

method not allowed

What it means

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.

Source

Thrown at modules/caddyhttp/reverseproxy/admin.go:66

}

// Routes returns a route for the /reverse_proxy/upstreams endpoint.
func (al adminUpstreams) Routes() []caddy.AdminRoute {
	return []caddy.AdminRoute{
		{
			Pattern: "/reverse_proxy/upstreams",
			Handler: caddy.AdminHandlerFunc(al.handleUpstreams),
		},
	}
}

// handleUpstreams reports the status of the reverse proxy
// upstream pool.
func (adminUpstreams) handleUpstreams(w http.ResponseWriter, r *http.Request) error {
	if r.Method != http.MethodGet {
		return caddy.APIError{
			HTTPStatus: http.StatusMethodNotAllowed,
			Err:        fmt.Errorf("method not allowed"),
		}
	}

	// Prep for a JSON response
	w.Header().Set("Content-Type", "application/json")
	enc := json.NewEncoder(w)

	// Collect the results to respond with
	results := []upstreamStatus{}
	knownHosts := make(map[string]struct{})

	// Iterate over the static upstream pool (needs to be fast)
	var rangeErr error
	hosts.Range(func(key, val any) bool {
		address, ok := key.(string)
		if !ok {
			rangeErr = caddy.APIError{
				HTTPStatus: http.StatusInternalServerError,

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Use a plain GET request: 'curl localhost:2019/reverse_proxy/upstreams'.
  2. Remove -X POST/-d flags from monitoring/health-check commands hitting read-only admin endpoints.
  3. Treat a 405 from this endpoint as a client bug, not a server problem.

Example fix

# before
curl -X POST http://localhost:2019/reverse_proxy/upstreams -d '{}'

# after
curl http://localhost:2019/reverse_proxy/upstreams
Defensive patterns

Strategy: validation

Validate before calling

// client side: never send a body or non-GET method to this endpoint
req, _ := http.NewRequest(http.MethodGet, adminURL+"/reverse_proxy/upstreams", nil)
// server side (if wrapping admin endpoints):
if r.Method != http.MethodGet {
    w.Header().Set("Allow", http.MethodGet)
    http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}

Try / catch

resp, err := http.Get(url)
if err == nil && resp.StatusCode == http.StatusMethodNotAllowed {
    // fix the caller: this endpoint is read-only GET
    log.Print("/reverse_proxy/upstreams is GET-only; remove -X/-d from the request")
}

Prevention

When it happens

Trigger: Sending POST, PUT, DELETE, etc. to the admin endpoint on the admin listener, e.g. 'curl -X POST localhost:2019/reverse_proxy/upstreams'.

Common situations: Monitoring scripts that default to POST with a JSON body; curl invocations with -X flags copied from write-style admin endpoints like /load.

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/6d0fe7bb8b3328dd. Report an issue: GitHub.