cloudflare/cloudflared · error

JWKS endpoint %s returned status %d

Error message

JWKS endpoint %s returned status %d

What it means

HTTP-level failure in fetchJWKS: the auth domain's certs endpoint (JWKS) answered with a non-200 status. The access JWT used for `cloudflared tunnel token`/access flows cannot be verified because the signing keys were not retrieved — could be a redirect (redirects are intentionally not followed), server error, or wrong auth domain.

Source

Thrown at token/jwks.go:111

// 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")
	}
	return &keySet, nil
}

// jwksCachePath returns the on-disk path for cached JWKS for the given auth domain.

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Check the auth domain URL and that the /cdn-cgi/access/certs endpoint is reachable.
  2. A 3xx status means redirects are blocked by design — verify the domain is not redirecting.
  3. Retry later on 5xx; callers with caching will refresh via verifyMetadataWithRetry.
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at token/jwks.go:111 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/0be97d28e84d62a7. Report an issue: GitHub.