Skip to content

Nightly web bundle initializes production Clerk on localhost with no runtime opt-out #3826

Description

@zzjin

Summary

Running the nightly t3 package locally serves a bundled web UI that initializes Clerk against https://clerk.t3.codes, even when the app is opened from a non-t3.codes origin such as http://localhost:3773.

Because the bundled Clerk publishable key appears to be a production key restricted to t3.codes, Clerk rejects the browser origin and returns 400s during startup:

GET https://clerk.t3.codes/v1/environment?__clerk_api_version=2026-05-12&_clerk_js_version=6.25.0 400
GET https://clerk.t3.codes/v1/client?__clerk_api_version=2026-05-12&_clerk_js_version=6.25.0 400

Clerk: Production Keys are only allowed for domain "t3.codes".
API Error: The Request HTTP Origin header must be equal to or a subdomain of the requesting URL.

There does not seem to be a runtime environment variable or CLI flag equivalent to T3CODE_TELEMETRY_ENABLED=false for disabling Clerk / T3 Connect in a prebuilt nightly package.

Environment

  • Package: nightly t3
  • Server version observed from /.well-known/t3/environment:
{
  "serverVersion": "0.0.29-nightly.20260709.766"
}
  • Local URL:
http://localhost:3773

Steps to Reproduce

  1. Start the nightly package locally, for example:
T3CODE_TELEMETRY_ENABLED=false bunx t3@nightly serve

or the equivalent nightly bunx command.

  1. Open the locally served web app:
http://localhost:3773/
  1. Open browser DevTools.

Actual Behavior

The web bundle loads Clerk from clerk.t3.codes and then attempts to fetch Clerk environment/client state from the same production Clerk frontend API. On localhost, Clerk rejects the request because the production key is only allowed for t3.codes.

Observed network requests include:

GET http://localhost:3773/assets/index-*.js 200
GET https://clerk.t3.codes/npm/@clerk/clerk-js@6.25.0/dist/clerk.browser.js 200
GET https://clerk.t3.codes/npm/@clerk/ui@1.25.0/dist/ui.browser.js 200
GET https://clerk.t3.codes/v1/environment?... 400
GET https://clerk.t3.codes/v1/client?... 400

In my browser this shows up as Clerk warnings/errors during startup. In some sessions/users' browsers, the app can appear to render no useful content, making this look like a local startup failure.

Expected Behavior

Local/nightly usage should not try to initialize a production Clerk instance that is only valid for t3.codes, unless the current origin is supported or the user explicitly enables T3 Connect.

At minimum, there should be a documented runtime opt-out for prebuilt packages, for example one of:

T3CODE_CONNECT_ENABLED=false
T3CODE_CONNECT_DISABLED=true
T3CODE_CLERK_ENABLED=false
T3CODE_DISABLE_CLERK=true

The opt-out should prevent the served web app from mounting ClerkProvider and should hide/disable T3 Connect UI affordances.

Why This Happens

From reading the current code, the web app enables Clerk when the bundled public cloud config is complete:

  • apps/web/src/main.tsx reads import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
  • apps/web/src/main.tsx mounts ClerkProvider when clerkPublishableKey && hasCloudPublicConfig()
  • apps/web/src/cloud/publicConfig.ts makes hasCloudPublicConfig() true when these are present:
    • VITE_CLERK_PUBLISHABLE_KEY
    • VITE_CLERK_JWT_TEMPLATE
    • VITE_T3CODE_RELAY_URL
  • apps/web/vite.config.ts injects those values at build time via define

For source builds this can be controlled by leaving the env vars unset before building. For a nightly package, the web bundle is already built, so runtime env vars such as clearing T3CODE_CLERK_PUBLISHABLE_KEY or VITE_CLERK_PUBLISHABLE_KEY do not affect the browser bundle.

The server also appears to serve the static client as built:

  • apps/server/src/http.ts returns files from dist/client
  • no runtime template replacement appears to happen for the Clerk/web public config

So there is currently no supported way to run a prebuilt nightly package locally while disabling Clerk/T3 Connect in the browser.

Suggested Fixes

A few possible approaches:

  1. Add a runtime/public config endpoint and make the web app decide whether to mount Clerk at runtime instead of relying only on Vite build-time constants.

  2. Add a runtime opt-out env var, such as:

T3CODE_CONNECT_DISABLED=true

and have the server expose that to the frontend or strip/override the public cloud config before serving the app.

  1. Do not include production Clerk public config in nightly/local bundles by default. Only include it for hosted t3.codes deployments.

  2. Make Clerk initialization origin-aware. If the current origin is not https://t3.codes or an allowed subdomain, skip ClerkProvider and treat T3 Connect as unavailable.

Workaround

There does not appear to be a clean runtime workaround for prebuilt nightly packages.

T3CODE_TELEMETRY_ENABLED=false correctly disables server telemetry, but it does not affect the bundled web Clerk initialization.

Browser request blocking can hide some Clerk 400 noise, but it is not a real fix and can break Clerk script loading if applied too broadly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    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