caddyserver/caddy · error
server responded with HTTP %d
Error message
server responded with HTTP %d
What it means
The HTTP config loader (loading the running config over HTTP(S)) treats a final response with status >= 400 as a hard failure, reporting the status code. Because attemptHttpCall already rejects anything outside 200-499 and retries it, the responses that actually reach this check are 4xx ones (404, 401, 403, ...) — client-side errors that retrying will not fix.
Source
Thrown at caddyconfig/httploader.go:114
url := repl.ReplaceAll(hl.URL, "")
req, err := http.NewRequestWithContext(ctx, method, url, nil)
if err != nil {
return nil, err
}
for key, vals := range hl.Headers {
for _, val := range vals {
req.Header.Add(repl.ReplaceAll(key, ""), repl.ReplaceKnown(val, ""))
}
}
resp, err := doHttpCallWithRetries(ctx, client, req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
return nil, fmt.Errorf("server responded with HTTP %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// adapt the config based on either manually-configured adapter or server's response header
ct := resp.Header.Get("Content-Type")
if hl.Adapter != "" {
ct = "text/" + hl.Adapter
}
result, warnings, err := adaptByContentType(ct, body)
if err != nil {
return nil, err
}
for _, warn := range warnings {
ctx.Logger().Warn(warn.String())View on GitHub (pinned to 50e54ee279)
Solutions
- curl the exact URL from the Caddy host and inspect status/body; fix the path or endpoint.
- If auth is required, configure the loader's 'headers' block (e.g. Authorization bearer token).
- Check the config server's logs for why it rejected the request.
Example fix
# before
{
admin off
config_loaders {
http http://cfg.internal/caddy/config
}
}
# after
{
admin off
config_loaders {
http http://cfg.internal/caddy/config {
headers Authorization "Bearer {env.CFG_TOKEN}"
}
}
} Defensive patterns
Strategy: retry
Validate before calling
curl -fsS -H "Authorization: Bearer $CFG_TOKEN" "$CONFIG_URL" >/dev/null && echo endpoint-ok || echo endpoint-broken
Try / catch
// if embedding Caddy: treat config-load status errors as non-retryable client errors
if err := loadConfig(ctx); err != nil {
if strings.Contains(err.Error(), "server responded with HTTP") {
// 4xx from the config endpoint: fix URL/headers, do not retry blindly
log.Fatal(err)
}
return err
} Prevention
- Health-check the config endpoint (with the same headers) before (re)starting Caddy.
- Send required auth headers via the http loader's headers option.
- Keep the config endpoint returning 2xx for success.
When it happens
Trigger: Running Caddy with a config URL where the endpoint returns 404 (wrong path), 401/403 (auth required but no/insufficient headers configured), or any 4xx on the first attempt.
Common situations: Centralized config distribution where the config service requires a token not sent; typo in the config URL path; endpoint moved after a config-service upgrade.
Related errors
- problem calling http loader url: %v
- bad response status code from http loader url: %v
- HTTP %d fetching CA certificate bundle from %s
- got HTTP %d
- loading dynamic config from %T: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/c1b31bae088cbeab.
Report an issue: GitHub.