Preview URLs: Authorization header silently rewritten by edge regardless of basicAuth/bearerToken config
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- next.js, typescript
Research direction
Start at the SDK's getPublicURL(port, options) entry point and reproduce the behavior with the curl examples against a default preview URL. Check the preview API response for auth fields, then trace where the edge behavior is configured; done means default previews either forward the client's Authorization header unchanged or clearly document and expose the intended behavior.
Written by the indexing model from the issue text.
Description
Problem
Public preview URLs are documented as supporting three auth modes via box.getPublicURL(port, options):
- Default (no options): public URL, no auth.
{ bearerToken: true }: Upstash issues a Bearer token.{ basicAuth: true }: Upstash issuesusername+password.
When mode 1 (default) is used, the URL is documented as fully public with no Authorization enforcement. In practice, Upstash's preview edge silently overwrites whatever Authorization header the client sends and replaces it with a Basic auth header before the request reaches the box's dev server.
Repro
Create a box, expose a port with no auth options:
const box = await Box.create({ ... });
await box.getPublicURL(3000); // no options
Inside the box, run a Next.js dev server that echoes request headers. Then from outside:
curl -H "Authorization: Bearer my-jwt-token" https://<box>-3000.preview.box.upstash.com/api/echo-headers
Expected: the dev server sees Authorization: Bearer my-jwt-token.
Actual: the dev server sees Authorization: Basic admin:<64-char-hex>. The original client header is gone. The injected Basic value is the same on every request for a given box, suggesting it is a per-box internal proxy credential rather than dynamic.
Same behavior with no Authorization header at all on the client side:
curl https://<box>-3000.preview.box.upstash.com/api/echo-headers
The dev server still sees Authorization: Basic admin:<hex>.
Confirmation that the URL has no auth configured
Listing the box's public URLs via the SDK shows no token, username, or password fields:
curl -H "Authorization: Bearer $UPSTASH_BOX_API_KEY" \
https://<region>.box.upstash.com/v2/box/<box-id>/preview
Returns:
{
"previews": [
{
"id": "<box-id>-3000",
"box_id": "<box-id>",
"port": 3000,
"created_at": <ts>
}
]
}
No bearer_token, no basic_auth. So this is not the expected behavior of any documented auth mode.
Impact
Any user app that uses the industry-standard Authorization: Bearer <jwt> pattern for its own auth (Auth.js, NextAuth, JWT-based sessions, custom JWT routes, etc.) works correctly when deployed to Vercel / Cloudflare Pages / anywhere else, but returns 401 on every authenticated request when viewed inside the preview iframe. The JWT never reaches the app's verifyToken because Upstash has already replaced the header with its own Basic credential.
This breaks AI coding agent platforms that build apps and let users test them in a sandbox preview. The "build a TODO app with login" flow works end to end on the deployed site but fails to load any data in the preview because every fetch returns 401. Users report this as a platform bug when it is actually Upstash silently mangling the header.
We hit this in production on Automatio and chased it through the wrong layers (our CF Worker proxy, env vars, AUTH_SECRET) for hours before identifying Upstash as the source. We are filing it as a separate issue from #101 because the cause is different (header rewrite vs same-origin / CORS / WebSocket gaps), and the workaround is different too (change app code, not iframe / proxy config).
Workarounds we are aware of
These all require changes inside the user app:
- Send the token via a custom header like
X-Auth-Tokenand read it from there in backend routes. Upstash leavesX-Auth-Tokenalone. - Send the token via a cookie (
Set-Cookieon login response,req.cookieson backend). Cookies pass through Upstash untouched.
We do not want to force our coding agent to generate non-standard auth code (the Authorization: Bearer pattern is the right default for the deployed world), so we are blocked on this.
Asks
- Confirm whether the Basic header injection is intentional behavior for default public URLs. If yes, please document it so other platforms do not hit the same trap.
- If it is intentional, please consider adding an option to disable the rewrite for public URLs that opted out of auth.
- If it is unintentional, this is a bug worth fixing. The expected behavior for
getPublicURL(port)with no auth options is that the dev server sees exactly what the client sent.
Environment
@upstash/boxSDK current version- Verified on multiple boxes in multiple regions
Related: this is from the same "Upstash edge interferes with user app headers" family as upstash/box#101 (origin and CORS) and upstash/box#106 (x-forwarded-host being internal IP).
- Dominant language
- TypeScript
- Stars
- 45
- Forks
- 7
- Avg merge
- 2d 22h
- Merged PRs (30d)
- 10
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from upstash/box
-
Difficulty 1/5 Under an hour Newbie friendliness 45/100
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 55/100
upstash/box#226 · 5 comments ·
Maintainers usually reply within 1 day
-
PayG keepAlive=false boxes not auto-pausing at documented 1h idle timeout .. observed 2h+ still idleOpen
Difficulty 4/5 3-5 days Newbie friendliness 48/100
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 38/100
upstash/box#154 · 3 comments ·
Maintainers usually reply within 1 day
Similar issues
-
bug:unconfirmed
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Heroic-Games-Launcher/HeroicGamesLauncher#6062 ·
Maintainers usually reply within 1 day
-
enhancement good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
Richard-Zhang1019/Crossbow#431 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 63/100
EKKOLearnAI/ekko-studio#3761 ·
Maintainers usually reply within 1 day
-
Per line discountOpenenhancement good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
ruyd/agentic-sdlc-lab#4 · 2 comments ·