Skip to content

access token: HEAD request to metadata endpoint times out with valid cached token (redirect follow bug) #1637

@thebenjaminlee

Description

@thebenjaminlee

Summary

cloudflared access token <url> times out on a metadata HEAD request even when a valid cached token exists in ~/.cloudflared/. The same request succeeds instantly via curl.

Version

cloudflared version 2026.3.0 (built 2026-03-06T12:53:40Z)
macOS Darwin 25.4.0 (arm64)

Reproduction

# 1. Login successfully (token cached)
cloudflared access login https://my-app.example.com

# 2. Verify cached token exists and is valid
ls ~/.cloudflared/my-app.example.com-*-token

# 3. Try to retrieve the token — times out
cloudflared access token https://my-app.example.com
# Error: failed to get app info: Head "https://org.cloudflareaccess.com/cdn-cgi/access/login/my-app.example.com?kid=...&meta=<jwt>&redirect_url=/": context deadline exceeded (Client.Timeout exceeded while awaiting headers)

Diagnosis

The failure is in the get app info step, which issues a HEAD request to:

https://<org>.cloudflareaccess.com/cdn-cgi/access/login/<hostname>?kid=<kid>&meta=<jwt>&redirect_url=/

This endpoint returns a 302 redirect to the protected origin (https://my-app.example.com/).

Via curl (works):

curl -s -o /dev/null -w "%{http_code} %{time_total}s" -I \
  "https://org.cloudflareaccess.com/cdn-cgi/access/login/my-app.example.com?kid=...&redirect_url=%2F"
# 302 0.5s

Via cloudflared (fails):
Go's net/http client follows the 302 by default. The redirect target is the CF Access-protected origin, which either:

  • Redirects back to the login endpoint (infinite loop), or
  • Requires a valid CF Access token to respond (chicken-and-egg: we're calling access token to get the token)

This causes the internal timeout.

Expected behavior

cloudflared access token should:

  1. Check if a valid (non-expired) cached token exists in ~/.cloudflared/
  2. If valid, return it without making any network requests
  3. Only hit the metadata endpoint when the token is expired or missing

The metadata HEAD should also use CheckRedirect to avoid following the 302, since the 302 itself confirms the app exists — the redirect target requires auth that the client doesn't have yet.

Workaround

Read the cached token directly and validate the JWT exp claim:

TOKEN_FILE=$(find ~/.cloudflared -name "my-app.example.com-*-token" | head -1)
TOKEN=$(cat "$TOKEN_FILE")
# Validate exp claim before using

Impact

This blocks all programmatic use of cloudflared access token when the metadata HEAD doesn't complete. Affects CI/CD pipelines, kubectl wrappers, and any automation that depends on cloudflared access token returning cached tokens reliably.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions