caddyserver/caddy · error
caddy responded with error: HTTP %d: %s
Error message
caddy responded with error: HTTP %d: %s
What it means
The admin API responded with a 4xx/5xx status and this message surfaces that status plus the first 2 MiB of the body. It is the CLI's generic relay of server-side errors from operations like loading a config (caddy reload / caddy load), stopping, or reading endpoints.
Source
Thrown at cmd/commandfuncs.go:850
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial(parsedAddr.Network, parsedAddr.JoinHostPort(0))
},
},
}
resp, err := client.Do(req) //nolint:gosec // the only SSRF here would be self-sabotage I think
if err != nil {
return nil, fmt.Errorf("performing request: %v", err)
}
// if it didn't work, let the user know
if resp.StatusCode >= 400 {
respBody, err := io.ReadAll(io.LimitReader(resp.Body, 1024*1024*2))
if err != nil {
return nil, fmt.Errorf("HTTP %d: reading error message: %v", resp.StatusCode, err)
}
return nil, fmt.Errorf("caddy responded with error: HTTP %d: %s", resp.StatusCode, respBody)
}
return resp, nil
}
// DetermineAdminAPIAddress determines which admin API endpoint address should
// be used based on the inputs. By priority: if `address` is specified, then
// it is returned; if `config` is specified, then that config will be used for
// finding the admin address; if `configFile` (and `configAdapter`) are specified,
// then that config will be loaded to find the admin address; otherwise, the
// default admin listen address will be returned.
func DetermineAdminAPIAddress(address string, config []byte, configFile, configAdapter string) (string, error) {
// Prefer the address if specified and non-empty
if address != "" {
return address, nil
}
// Try to load the config from file if specified, with the given adapter nameView on GitHub (pinned to 50e54ee279)
Solutions
- Read the body text in the message — it names the exact server-side rejection cause
- Validate the config first with: caddy validate --config <file>
- Check the daemon logs for the stack behind the 4xx/5xx
- Ensure the running binary includes the modules your config references (caddy list-modules)
Example fix
# before caddy reload --config Caddyfile # fails: caddy responded with error: HTTP 500 ... # after (catch config problems before reloading) caddy validate --config Caddyfile && caddy adapt --config Caddyfile --adapter caddyfile | jq . caddy reload --config Caddyfile
Defensive patterns
Strategy: validation
Validate before calling
# Validate before applying, so server-side 4xx/5xx never happens: caddy validate --config /etc/caddy/Caddyfile
Try / catch
err := reload(); if err != nil && strings.Contains(err.Error(), "caddy responded with error: HTTP 4") { /* config rejected: surface body, keep previous config running */ } Prevention
- Make 'caddy validate' a required CI step for every config change
- Parse the HTTP status and body out of the message for actionable logging
- Remember a failed reload leaves the old config active — fix the cause, don't just retry
When it happens
Trigger: 'caddy reload' with a config the running instance rejects (bad module names, invalid site addresses, provisioning failures); 'caddy stop' when already stopped (404 on /stop semantics); requesting a config path that does not exist.
Common situations: A Caddyfile that adapts fine but fails provisioning at runtime (e.g. TLS problem, port already bound by another process, unresolved module); reloading against a Caddy version lacking a module used in the config.
Related errors
- invalid admin address %s: %v
- no config file to load; either use --config flag or ensure C
- no metrics registry found
- loading identity issuer modules: %s
- access control %d public key %d: parsing base64 certificate
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/5c71ecd26346f52a.
Report an issue: GitHub.