All guidesMore providers: databases, auth, hosting, email and payments
On this page

Clerk error

Clerk

Clerk: Production Keys are only allowed for domain

By Ohad Krispin, founder of VibeRaven · LinkedIn

"Clerk: Production Keys are only allowed for domain" is thrown by clerk-js in the browser when Clerk's Frontend API rejects the page origin with the error code origin_invalid and the app uses a pk_live_ publishable key. Clerk's docs say production keys work only with the production domain you set in the Clerk Dashboard, and with its subdomains, so localhost is rejected, and it is currently not possible to use production keys with a host's own preview domain such as *.vercel.app. Scope Clerk's development keys (pk_test_ and sk_test_) to the Vercel Preview and Development environments, keep pk_live_ and sk_live_ on Production only, and redeploy. The domain in the message is the Frontend API host from the key, without the clerk. prefix.

The full message in the browser console

The code is in packages/clerk-js/src/core/resources/Base.ts. It runs when a Frontend API response fails with the code origin_invalid and the publishable key is a production key. The second line is the message Clerk's API sent back.

Clerk: Production Keys are only allowed for domain "example.com".
API Error: <message from the Clerk API>

Can I use production keys on a Vercel preview deployment?

Clerk's environments guide gives two routes. To share the production settings and users, put the preview on a subdomain of the production root domain with the same keys as production; on Vercel that uses the Preview Deployment Suffix feature, which Clerk says is only available on the Pro and Enterprise plans. For independent settings and users, use the *.vercel.app preview domain with development keys,.

Can I run production keys on localhost?

Clerk's troubleshooting page calls it strongly discouraged and not officially supported, and meant only for debugging problems you cannot reproduce in development. It needs a subdomain of the production domain mapped to 127.0.0.1 in the hosts file and the dev server on HTTPS port 443, because a port like 3000 is included in the Origin header and fails the check. For normal local work, use the development keys.

Checklist

How to fix it, then check the fix

  1. 01
    Find any live key outside Vercel's Production scope, such as a pk_live_ value in a committed .env file or in your local .env.local:
    grep -rnE "pk_live_|sk_live_" --exclude-dir=node_modules --exclude-dir=.next .
  2. 02In Vercel, open the project's Environment Variables. Set NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY to the production pair for Production only. Clerk's Vercel guide says each key can be tied to specific Vercel environments.
  3. 03
    Add the development pair, pk_test_ and sk_test_ from the development instance, for Preview and Development:
    vercel env add NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY preview
    vercel env add CLERK_SECRET_KEY preview
    vercel env add NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY development
    vercel env add CLERK_SECRET_KEY development
  4. 04Keep production keys out of the .env.local you use on localhost. Vercel's vercel env pull downloads the Development variables, so the development pair is what lands on your machine.
  5. 05Push a commit to get a new deployment, because Vercel applies changed variables only to new deployments. Open the preview and the production site and check that the browser console no longer shows the message.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

Does it work on a subdomain of my production domain?

Yes. Clerk's troubleshooting page says subdomains of the production domain work, because authentication across subdomains is enabled by default in production instances.

Do I need to change anything for OAuth on previews?

Clerk's environments guide says development instances use Clerk's shared OAuth credentials for most social providers, and production needs your own. Social sign-in on a preview with development keys uses the shared ones, with their limits on extra scopes and access tokens.

Why is the domain in the message not the one I opened?

The message names the domain of the Frontend API behind your publishable key, with the clerk. prefix removed. It is the one production domain the key accepts, not the page you opened.