{"record":{"id":"f39155df2fa61e8e","repo":"caddyserver/caddy","slug":"http-d-fetching-ca-certificate-bundle-from-s","errorCode":null,"errorMessage":"HTTP %d fetching CA certificate bundle from %s","messagePattern":"HTTP (.+?) fetching CA certificate bundle from (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/capools.go","lineNumber":691,"sourceCode":"\thttpClient := *http.DefaultClient\n\thttpClient.Transport = customTransport\n\n\tfor _, uri := range hcp.Endpoints {\n\t\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tres, err := httpClient.Do(req) //nolint:gosec // SSRF false positive... uri comes from config\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpembs, err := io.ReadAll(res.Body)\n\t\tres.Body.Close()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif res.StatusCode < 200 || res.StatusCode >= 300 {\n\t\t\treturn fmt.Errorf(\"HTTP %d fetching CA certificate bundle from %s\", res.StatusCode, uri)\n\t\t}\n\t\t// Parse PEM to extract certificates\n\t\tpemData := pembs\n\t\tfor len(pemData) > 0 {\n\t\t\tvar block *pem.Block\n\t\t\tblock, pemData = pem.Decode(pemData)\n\t\t\tif block == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif block.Type != \"CERTIFICATE\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcert, err := x509.ParseCertificate(block.Bytes)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"parsing certificate from URL %s: %v\", uri, err)\n\t\t\t}\n\t\t\tcaPool.AddCert(cert)\n\t\t\tcerts = append(certs, cert)","sourceCodeStart":673,"sourceCodeEnd":709,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/capools.go#L673-L709","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\ntrust_pool http https://ca.internal/roots/bundle-crashed.pem\n\n# after\ntrust_pool http https://ca.internal/roots/bundle.pem   # verified with curl -I -> 200","handlingStrategy":"retry","validationCode":"// pre-flight the bundle URL from the Caddy host\nimport \"net/http\"\n\nfunc bundleURLReachable(u string) error {\n\tresp, err := http.Head(u) //nolint:gosec // operator-supplied URL\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\treturn fmt.Errorf(\"bundle URL returned HTTP %d\", resp.StatusCode)\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"// on reload failure due to transient upstream errors, keep previous config and retry the reload later\nif err := applyConfig(cfg); err != nil && strings.Contains(err.Error(), \"fetching CA certificate bundle\") {\n\tscheduleRetryReload(backoff) // previous good config stays active\n}","preventionTips":["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)."],"tags":["caddy","caddytls","network","http","status-code"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}