Skip to content

Bypass Cloudflare 403 by sending browser-like request headers - #110

Merged
emreertunc merged 2 commits into
Lampese:mainfrom
rushiranpise:fix/cloudflare-403-browser-headers
Jul 22, 2026
Merged

emreertunc merged 2 commits into
Lampese:mainfrom
rushiranpise:fix/cloudflare-403-browser-headers

Conversation

@rushiranpise

Copy link
Copy Markdown
Contributor

Problem

When refreshing account usage or metadata, requests to chatgpt.com/backend-api
were being blocked by Cloudflare with a 403 Forbidden response:

Enable JavaScript and cookies to continue

This happened because the requests used User-Agent: codex-cli/1.0.0 with no
other browser signals, which Cloudflare's bot detection immediately flags.

Fixes #108

Solution

Replace the bot-like UA and add the standard headers a real browser sends:

  • User-Agent — Chrome 136 on Windows (instead of codex-cli/1.0.0)
  • Accept — application/json, text/plain, */*
  • Accept-Language — en-US,en;q=0.9
  • Origin / Referer — https://chatgpt.com
  • sec-fetch-dest/mode/site — empty / cors / same-origin

Also adds a clear human-readable error message on the accounts/check endpoint
if a 403 still occurs, instead of surfacing a raw HTML blob to the user.

What was intentionally NOT changed

Token refresh is still triggered on 401 only. A 403 from Cloudflare does
not mean the token is expired — refreshing on 403 burns the refresh token and
causes a refresh_token_reused error on the next legitimate refresh.

Testing

Ran the production build locally and monitored stdout logs across multiple
refresh cycles. All accounts return 200 OK with zero 403s observed.

@emreertunc emreertunc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix. I found two issues that need to be addressed before approval.

  1. cargo fmt --manifest-path src-tauri/Cargo.toml -- --check currently fails. Please run cargo fmt --manifest-path src-tauri/Cargo.toml and commit the formatting changes.

  2. The new 403 error message says the session tokens are stale and tells users to remove and re-add the account. This contradicts the PR’s own explanation that a 403 is a Cloudflare challenge or permissions error, not an expired-token signal. The metadata flow also refreshes the token immediately before this request, so the guidance is misleading and may cause users to remove accounts unnecessarily.

Please change the message so it accurately describes the Cloudflare request failure without blaming stale tokens or recommending account removal.

@rushiranpise
rushiranpise force-pushed the fix/cloudflare-403-browser-headers branch from a8299fe to a8bee46 Compare July 22, 2026 23:39
Prior to this change every request to chatgpt.com/backend-api was sent
with User-Agent: codex-cli/1.0.0 and no other browser signals, which
reliably triggered Cloudflare's bot-detection challenge and returned a
403 Forbidden response with 'Enable JavaScript and cookies to continue'.

Changes in src-tauri/src/api/usage.rs:

- Replace CODEX_USER_AGENT ('codex-cli/1.0.0') with BROWSER_USER_AGENT,
  a current Chrome 136 / Windows 10 UA string that Cloudflare accepts.

- Add browser-like headers to build_chatgpt_headers():
    Accept: application/json, text/plain, */*
    Accept-Language: en-US,en;q=0.9
    Origin: https://chatgpt.com
    Referer: https://chatgpt.com
    sec-fetch-dest: empty
    sec-fetch-mode: cors
    sec-fetch-site: same-origin

- Apply the same browser UA to the API-key warmup path in
  warmup_with_api_key() for consistency.

- Add an explicit 403 guard in fetch_chatgpt_account_metadata() that
  surfaces a human-readable error instead of a raw HTML blob, so users
  understand what happened if a 403 still occurs after retry.

- Keep the 401-only token-refresh retry logic unchanged. A 403 is a
  Cloudflare/permissions signal and must NOT trigger a token refresh;
  doing so burns the refresh token and causes a refresh_token_reused
  error on the next legitimate refresh call.

Tested: all accounts return 200 OK after this change with zero 403s
observed across multiple refresh cycles (confirmed via stdout log).

Fixes: Lampese#108
cargo fmt:
- Collapsed multi-line use imports and headers.insert calls in usage.rs
  to satisfy cargo fmt --check (was failing CI)

Error message:
- Old: 'session tokens are stale, try removing and re-adding the account'
- New: 'request likely blocked by Cloudflare bot detection, transient
  network issue, try again in a moment'

The 403 is a Cloudflare bot-detection challenge, not an expired-token
signal. The metadata flow already refreshes tokens before this request
via ensure_chatgpt_tokens_fresh(), so blaming stale tokens was
inaccurate and could cause users to unnecessarily remove accounts.
@rushiranpise
rushiranpise force-pushed the fix/cloudflare-403-browser-headers branch from a8bee46 to e78ada8 Compare July 22, 2026 23:42
@rushiranpise

Copy link
Copy Markdown
Contributor Author

Thanks for the fix. I found two issues that need to be addressed before approval.

  1. cargo fmt --manifest-path src-tauri/Cargo.toml -- --check currently fails. Please run cargo fmt --manifest-path src-tauri/Cargo.toml and commit the formatting changes.
  2. The new 403 error message says the session tokens are stale and tells users to remove and re-add the account. This contradicts the PR’s own explanation that a 403 is a Cloudflare challenge or permissions error, not an expired-token signal. The metadata flow also refreshes the token immediately before this request, so the guidance is misleading and may cause users to remove accounts unnecessarily.

Please change the message so it accurately describes the Cloudflare request failure without blaming stale tokens or recommending account removal.

Thanks for the detailed review! Both issues are now addressed:

Formatting — ran cargo fmt and committed the result. cargo fmt --check now passes.

Error message — you're right, the old message was contradictory. A 403 from chatgpt.com is a Cloudflare bot-detection challenge, not a token expiry signal — especially since the code already calls ensure_chatgpt_tokens_fresh() before this request. Changed to: "Accounts check API returned 403 Forbidden. The request was likely blocked by Cloudflare bot detection. This is a transient network issue please try again in a moment."

@emreertunc
emreertunc merged commit fa0b705 into Lampese:main Jul 22, 2026
@emreertunc

Copy link
Copy Markdown
Collaborator

thanks for the contribution!

Kevanko added a commit to Kevanko/codex-switcher-fork that referenced this pull request Oct 13, 2026
- pin Tauri CLI to ~2.10.1 to match the 2.10 runtime; 2.9 built Linux bundles
  the updater could not identify (from upstream Lampese#190)
- updater shows 'installing' after the download instead of offering a restart
  while the installer is still running
- chatgpt.com requests send browser-like headers so Cloudflare stops answering
  with a 403 challenge (upstream Lampese#110); a challenge page that still slips
  through is treated as transient instead of greying the account out
- drop appmenu-gtk-module before GTK starts, which crashed on launch on Linux
  desktops with a global menu (upstream Lampese#191)
- unit tests for keeping MCP logins when switching Claude accounts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Accounts check API returns 403 Forbidden due to Cloudflare challenge

2 participants