cloudflare/cloudflared · error
metadata JWT verification failed: %w
Error message
metadata JWT verification failed: %w
What it means
GetAppInfo wraps verifyMetadataWithRetry errors: the metadata JWT's signature could not be verified against the JWKS published by the auth domain (including disk cache and retry attempts). This prevents an attacker-controlled server from spoofing app identity — only JWTs signed by the account's keys are accepted.
Source
Thrown at token/token.go:445
return nil, err
}
// Decode without verification to extract auth_domain for JWKS lookup.
unverified, err := decodeMetadataUnverified(rawJWT)
if err != nil {
return nil, err
}
// Parse auth_domain into the canonical hostname used for JWKS lookup.
authDomain, err := parseAuthDomain(unverified.AuthDomain)
if err != nil {
return nil, fmt.Errorf("metadata JWT auth_domain validation failed: %w", err)
}
// Verify the JWT signature against the JWKS (with disk cache + retry).
claims, err := verifyMetadataWithRetry(rawJWT, authDomain)
if err != nil {
return nil, fmt.Errorf("metadata JWT verification failed: %w", err)
}
// Verify the hostname in the JWT matches the URL we actually requested.
if !strings.EqualFold(claims.Hostname, reqURL.Hostname()) {
return nil, fmt.Errorf("metadata JWT hostname %q does not match request host %q", claims.Hostname, reqURL.Hostname())
}
if claims.Type != metadataMatchType {
return nil, fmt.Errorf("metadata JWT type %q is not match", claims.Type)
}
if claims.AUD == "" {
return nil, errors.New("metadata JWT aud is empty")
}
if err := validateMetadataIssuedAt(claims.IAT, time.Now()); err != nil {
return nil, err
}
appHostname := claims.AppHostname
if appHostname == "" {View on GitHub (pinned to 2253eeeb25)
Solutions
- Clear the cached JWKS file used by the disk cache so fresh keys are fetched, then retry.
- Confirm the JWKS endpoint is reachable: curl https://<team-domain>/cdn-cgi/access/certs.
- Remove any TLS-intercepting proxy or add its CA properly so JWKS fetches are not tampered with.
- Check /etc/hosts and DNS for the auth domain — it must resolve to Cloudflare.
- Retry after a few seconds (transient edge issues) and update cloudflared if the edge switched signing algorithms.
Example fix
// before: stale key cache causes verification failure
info, err := token.GetAppInfo(appURL)
// after: verify JWKS reachability and clear stale cache
resp, err := http.Get("https://" + authDomain + "/cdn-cgi/access/certs")
if err != nil || resp.StatusCode != 200 {
return fmt.Errorf("JWKS unreachable for %s", authDomain)
}
os.Remove(jwksCachePath)
info, err := token.GetAppInfo(appURL) Defensive patterns
Strategy: retry
Validate before calling
resp, err := http.Get("https://" + authDomain + "/cdn-cgi/access/certs")
if err != nil || resp.StatusCode != http.StatusOK {
return fmt.Errorf("JWKS endpoint unreachable for auth domain %s", authDomain)
} Try / catch
var info *token.AppInfo
var err error
for i := 0; i < 3; i++ {
info, err = token.GetAppInfo(appURL)
if err == nil || !strings.Contains(err.Error(), "metadata JWT verification failed") {
break
}
os.Remove(jwksCachePath) // drop possibly stale cached keys
time.Sleep(time.Duration(1<<i) * time.Second)
}
if err != nil {
return err
} Prevention
- Keep the JWKS disk cache writable and clear it after Cloudflare key rotations.
- Ensure the team domain's /cdn-cgi/access/certs endpoint is reachable from the network.
- Avoid TLS-intercepting proxies on the auth domain, or trust their CA correctly.
- Keep NTP/clock sync so time-based claims validate.
- Retry with backoff — the library already retries, but stale caches need manual clearing.
When it happens
Trigger: Calling GetAppInfo when the JWT's kid is absent from the fetched JWKS, the JWKS endpoint is unreachable or returns errors, the JWT is malformed/tampered, keys were rotated and the disk cache is stale, or the auth domain resolved to the wrong Cloudflare account.
Common situations: Cloudflare key rotation while a cached stale JWKS is on disk, DNS/hosts file pointing the auth domain elsewhere, an intercepting TLS proxy (Zscaler/Netskope) breaking JWKS retrieval, firewalls blocking the team domain's /cdn-cgi/access/certs endpoint, or clock skew affecting token claims.
Related errors
- metadata JWT auth_domain validation failed: %w
- metadata JWT hostname %q does not match request host %q
- auth_domain %q does not end with %q
- invalid token
- metadata JWT aud is empty
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/c3d8b22244928323.
Report an issue: GitHub.