cloudflare/cloudflared · error
failed to fetch JWKS from %s
Error message
failed to fetch JWKS from %s
What it means
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.
Source
Thrown at token/jwks.go:106
return url.URL{}, fmt.Errorf("auth_domain %q does not end with %q", authDomain, accessDomainSuffix)
}
return url.URL{Scheme: httpsScheme, Host: hostname}, nil
}
// fetchJWKS fetches the JWKS from the auth domain's certs endpoint over HTTPS.
func fetchJWKS(authDomain url.URL) (*jose.JSONWebKeySet, error) {
jwksURL := authDomain
jwksURL.Path = accessCertPath
client := &http.Client{
CheckRedirect: func(_ *http.Request, _ []*http.Request) error {
return http.ErrUseLastResponse
},
Timeout: time.Second * 10,
}
resp, err := client.Get(jwksURL.String()) // nolint: gosec
if err != nil {
return nil, errors.Wrapf(err, "failed to fetch JWKS from %s", jwksURL.String())
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("JWKS endpoint %s returned status %d", jwksURL.String(), resp.StatusCode)
}
body, err := io.ReadAll(io.LimitReader(resp.Body, maxJWKSResponseSize+1))
if err != nil {
return nil, errors.Wrap(err, "failed to read JWKS response body")
}
if len(body) > maxJWKSResponseSize {
return nil, fmt.Errorf("JWKS response body exceeds %d bytes", maxJWKSResponseSize)
}
var keySet jose.JSONWebKeySet
if err := json.Unmarshal(body, &keySet); err != nil {
return nil, errors.Wrap(err, "failed to parse JWKS")View on GitHub (pinned to 2253eeeb25)
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.
Example fix
// ensure proxy-aware client when egress requires it
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
CheckRedirect: func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse },
Timeout: 10 * time.Second,
}
resp, err := client.Get(jwksURL.String()) Defensive patterns
Strategy: retry
Validate before calling
func canReachAuthDomain(authDomain string) error {
conn, err := net.DialTimeout("tcp", authDomain+":443", 5*time.Second)
if err != nil { return err }
return conn.Close()
} Try / catch
keySet, err := fetchJWKS(authDomain)
if err != nil && strings.Contains(err.Error(), "failed to fetch JWKS") {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
// retry with backoff; or serve from cached JWKS while offline
}
return fmt.Errorf("JWKS fetch failed (network/proxy/DNS): %w", err)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- failed to find Access application at %s
- failed to read JWKS response body
- failed to get app info
- quick tunnel provisioning failed with status %d: %s
- quick tunnel provisioning failed: %s
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/cd88ba81fe01d486.
Report an issue: GitHub.