{"record":{"id":"cd88ba81fe01d486","repo":"cloudflare/cloudflared","slug":"failed-to-fetch-jwks-from-s","errorCode":null,"errorMessage":"failed to fetch JWKS from %s","messagePattern":"failed to fetch JWKS from (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"token/jwks.go","lineNumber":106,"sourceCode":"\t\treturn url.URL{}, fmt.Errorf(\"auth_domain %q does not end with %q\", authDomain, accessDomainSuffix)\n\t}\n\treturn url.URL{Scheme: httpsScheme, Host: hostname}, nil\n}\n\n// fetchJWKS fetches the JWKS from the auth domain's certs endpoint over HTTPS.\nfunc fetchJWKS(authDomain url.URL) (*jose.JSONWebKeySet, error) {\n\tjwksURL := authDomain\n\tjwksURL.Path = accessCertPath\n\n\tclient := &http.Client{\n\t\tCheckRedirect: func(_ *http.Request, _ []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t},\n\t\tTimeout: time.Second * 10,\n\t}\n\tresp, err := client.Get(jwksURL.String()) // nolint: gosec\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed to fetch JWKS from %s\", jwksURL.String())\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"JWKS endpoint %s returned status %d\", jwksURL.String(), resp.StatusCode)\n\t}\n\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, maxJWKSResponseSize+1))\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to read JWKS response body\")\n\t}\n\tif len(body) > maxJWKSResponseSize {\n\t\treturn nil, fmt.Errorf(\"JWKS response body exceeds %d bytes\", maxJWKSResponseSize)\n\t}\n\n\tvar keySet jose.JSONWebKeySet\n\tif err := json.Unmarshal(body, &keySet); err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to parse JWKS\")","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/token/jwks.go#L88-L124","documentation":"fetchJWKS retrieves the JSON Web Key Set from the auth domain's /cdn-cgi/access/certs endpoint using an http.Client with a 10s timeout and redirects disabled. This wrapped error means the HTTP transport itself failed — DNS failure, connection refused/timeout, TLS handshake error, or a client-side abort — before any HTTP status was received.","triggerScenarios":"fetchJWKS (via getJWKSWithCache or verifyMetadataWithRetry) when client.Get(jwksURL) returns a transport error: no network/route to the auth domain, DNS resolution failure, egress firewall or proxy blocking the request, the 10-second timeout elapsing, or an invalid TLS certificate on the endpoint.","commonSituations":"Cloudflared running in an air-gapped or firewalled environment; corporate proxy without HTTPS_PROXY configured; DNS not resolving the *.cloudflareaccess.com domain; IPv6-only or broken DNS in containers; intermittent network flaps during token validation.","solutions":["Test reachability: `curl -v https://<team>.cloudflareaccess.com/cdn-cgi/access/certs` from the same host.","Configure proxy environment variables (HTTPS_PROXY) if egress goes through a corporate proxy, or open egress to the auth domain on port 443.","Fix DNS (check /etc/resolv.conf, try a public resolver) if the hostname does not resolve.","If errors are transient, retry — getJWKSWithCache will reuse a fresh cached JWKS while the network is down (valid for 24h).","Investigate TLS interception devices whose CA is not in the trust store if the error is a certificate error."],"exampleFix":"// ensure proxy-aware client when egress requires it\nclient := &http.Client{\n    Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},\n    CheckRedirect: func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse },\n    Timeout: 10 * time.Second,\n}\nresp, err := client.Get(jwksURL.String())","handlingStrategy":"retry","validationCode":"func canReachAuthDomain(authDomain string) error {\n    conn, err := net.DialTimeout(\"tcp\", authDomain+\":443\", 5*time.Second)\n    if err != nil { return err }\n    return conn.Close()\n}","typeGuard":null,"tryCatchPattern":"keySet, err := fetchJWKS(authDomain)\nif err != nil && strings.Contains(err.Error(), \"failed to fetch JWKS\") {\n    var netErr net.Error\n    if errors.As(err, &netErr) && netErr.Timeout() {\n        // retry with backoff; or serve from cached JWKS while offline\n    }\n    return fmt.Errorf(\"JWKS fetch failed (network/proxy/DNS): %w\", err)\n}","preventionTips":["Allow egress to *.cloudflareaccess.com:443 in firewalls and security groups.","Set HTTPS_PROXY/NO_PROXY correctly in containerized and corporate environments.","Verify DNS resolution inside containers before deploying.","Keep a warm JWKS cache (valid 24h) so brief outages do not break token validation."],"tags":["network","http","jwks","dns","cloudflare-access"],"backgroundTag":"http-request-failed","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}