caddyserver/caddy · error
HTTP %d fetching CA certificate bundle from %s
Error message
HTTP %d fetching CA certificate bundle from %s
What it means
The `tls.ca_pool.source.http` (HTTPCertPool) module fetched the configured URL to retrieve a CA bundle, and the response status was outside 2xx. The bundle cannot be trusted or parsed from an unsuccessful response.
Source
Thrown at modules/caddytls/capools.go:691
httpClient := *http.DefaultClient
httpClient.Transport = customTransport
for _, uri := range hcp.Endpoints {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return err
}
res, err := httpClient.Do(req) //nolint:gosec // SSRF false positive... uri comes from config
if err != nil {
return err
}
pembs, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
return err
}
if res.StatusCode < 200 || res.StatusCode >= 300 {
return fmt.Errorf("HTTP %d fetching CA certificate bundle from %s", res.StatusCode, uri)
}
// Parse PEM to extract certificates
pemData := pembs
for len(pemData) > 0 {
var block *pem.Block
block, pemData = pem.Decode(pemData)
if block == nil {
break
}
if block.Type != "CERTIFICATE" {
continue
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return fmt.Errorf("parsing certificate from URL %s: %v", uri, err)
}
caPool.AddCert(cert)
certs = append(certs, cert)View on GitHub (pinned to 50e54ee279)
Solutions
- curl -I the exact configured URL from the Caddy host and confirm it returns 2xx with the PEM bundle.
- Fix the path/host or move the bundle to a reachable unauthenticated URL.
- If the failure is transient, simply reload/retry after the endpoint recovers; consider serving the bundle locally (file pool) if the remote is unreliable.
Example fix
# before trust_pool http https://ca.internal/roots/bundle-crashed.pem # after trust_pool http https://ca.internal/roots/bundle.pem # verified with curl -I -> 200
Defensive patterns
Strategy: retry
Validate before calling
// pre-flight the bundle URL from the Caddy host
import "net/http"
func bundleURLReachable(u string) error {
resp, err := http.Head(u) //nolint:gosec // operator-supplied URL
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("bundle URL returned HTTP %d", resp.StatusCode)
}
return nil
} Try / catch
// on reload failure due to transient upstream errors, keep previous config and retry the reload later
if err := applyConfig(cfg); err != nil && strings.Contains(err.Error(), "fetching CA certificate bundle") {
scheduleRetryReload(backoff) // previous good config stays active
} Prevention
- Health-check the bundle endpoint as part of deploy gates.
- Serve bundles from highly available endpoints or mirror them locally (file pool) as fallback.
- Ensure the URL requires no auth and returns 200 directly (no login redirects).
When it happens
Trigger: The URL returns 404 (wrong path), 401/403 (auth required), 5xx (server error), or a redirect chain ending in a non-2xx — any status <200 or >=299 triggers this.
Common situations: Bundle moved or URL typo'd; the endpoint requires authentication headers the module does not send; transient upstream outages; rate-limiting (429) from public bundle hosts.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- got HTTP %d
- server responded with HTTP %d
- parsing certificate from URL %s: %v
- error reading response body: %v
- WebSocket connections aren't allowed.
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/f39155df2fa61e8e.
Report an issue: GitHub.