Guides

Production Deployment

Checklist and best practices for deploying Nuxt Better Auth in production.

Use this guide when your auth flow works locally and you are preparing to ship it to a real environment.

Environment Variables

Production requires an auth secret at runtime. On Nuxt 4.6+, the module derives it from Nuxt's appSecret when no auth secret is configured:

.env.production
# Required: 32+ character secret. On Nuxt 4.6+, Nuxt's application secret is enough
NUXT_APP_SECRET="your-32-character-secret-here-minimum"

# Dedicated auth secret (required before Nuxt 4.6, takes precedence over NUXT_APP_SECRET)
# NUXT_BETTER_AUTH_SECRET="your-32-character-secret-here-minimum"

# Alternative for non-destructive rotation
# BETTER_AUTH_SECRETS="2:current-secret-must-be-at-least-32-characters,1:previous-secret-must-be-at-least-32-characters"

# Required on Workers, custom domains, and hosts without a platform URL
NUXT_PUBLIC_SITE_URL="https://your-app.com"

# OAuth provider credentials (if using)
GOOGLE_CLIENT_ID="..."
GOOGLE_CLIENT_SECRET="..."

Generate a Secure Secret

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Singular secrets must be at least 32 characters. Shorter NUXT_BETTER_AUTH_SECRET or BETTER_AUTH_SECRET values cause auth initialization to throw at runtime, and Nuxt rejects a NUXT_APP_SECRET shorter than 32 characters. Better Auth validates versioned BETTER_AUTH_SECRETS/secrets entries and warns when the current key is too short.

Keep the secret stable across deployments. If a deployment already sets NUXT_BETTER_AUTH_SECRET or BETTER_AUTH_SECRET, keep it: removing it switches to the secret derived from NUXT_APP_SECRET, which signs users out and makes data encrypted with the old secret unreadable.

Security Checklist

Before Deploying

  • NUXT_APP_SECRET (Nuxt 4.6+), NUXT_BETTER_AUTH_SECRET, BETTER_AUTH_SECRET, BETTER_AUTH_SECRETS, or defineServerAuth({ secrets }) is configured
  • NUXT_PUBLIC_SITE_URL is set at runtime. For platform domains, VERCEL_URL (Vercel), CF_PAGES_URL (Cloudflare Pages), or URL (Netlify) can supply the URL instead. Cloudflare Workers needs an explicit URL, including on workers.dev.
  • trustedOrigins includes every active frontend origin (primary domain and preview domain, if used)
  • OAuth redirect URIs configured for production domain
  • NODE_ENV=production is set (disables devtools)

Route Protection

Route rules and definePageMeta are for UX (redirects). Always protect API endpoints with requireUserSession:

server/api/protected.get.ts
export default defineEventHandler(async (event) => {
  const { user } = await requireUserSession(event)
  return { data: 'protected' }
})

Rate Limiting

Better Auth enables its built-in rate limiter by default in production with a 60-second window and a maximum of 100 requests. It is disabled by default in development.

Better Auth stores rate-limit data in memory by default. For serverless or multi-instance deployments, configure database, secondary storage, or custom storage instead of relying on separate per-instance counters.

Separate Build and Runtime Environments

Some platforms, including Cloudflare Workers Builds, expose build-time and runtime environment variables separately. Your singular or versioned auth secret must be available to the deployed runtime, but it does not need to be present in the build container.

Trusted Origins for Preview Environments

If your workflow uses preview URLs (for example, *.workers.dev), include those origins in your Better Auth server config.

server/auth.config.ts
import { defineServerAuth } from '@nuxtjs/better-auth/config'

export default defineServerAuth({
  trustedOrigins: [
    'https://your-app.com',
    'https://your-preview.workers.dev',
  ],
})

Without the preview origin, browser auth flows can fail because Better Auth rejects cookie-based requests from unknown origins.

NuxtHub Deployment

When deploying with NuxtHub:

  1. Database migrations run automatically during build
  2. Set environment variables in your deployment platform
  3. Ensure @nuxthub/core is listed before @nuxtjs/better-auth in modules
nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxthub/core',  // Must be first
    '@nuxtjs/better-auth',
  ],
})
NuxtHub skips build-time migrations for Cloudflare D1. Run them before deployment with wrangler d1 migrations apply or a credentialed npx nuxt db migrate step.

Common Issues

"Singular auth secret must be at least 32 characters"

Your NUXT_BETTER_AUTH_SECRET or BETTER_AUTH_SECRET is too short. Generate a new one using the command above. This error is raised when auth initializes at runtime. NUXT_BETTER_AUTH_SECRET remains the recommended variable.

"An auth secret is required in production"

The deployed server runtime could not resolve an auth secret. Set NUXT_BETTER_AUTH_SECRET, BETTER_AUTH_SECRET, BETTER_AUTH_SECRETS, or secrets in defineServerAuth. On Nuxt 4.6+, setting NUXT_APP_SECRET is also enough.

On Nuxt 4.6+, the secret derived from NUXT_APP_SECRET is resolved asynchronously when the server starts. If the error says serverAuth() was called before it was ready, the call ran outside a request before the secret was derived, for example synchronously in a Nitro plugin. Use await ensureServerAuth() there, or set NUXT_BETTER_AUTH_SECRET.

"siteUrl required in production"

Set NUXT_PUBLIC_SITE_URL to your production domain for Cloudflare Workers, custom domains, and hosts without a supported platform URL variable. For platform domains, the module can use VERCEL_URL (Vercel), CF_PAGES_URL (Cloudflare Pages), or URL (Netlify) when available at runtime. Cloudflare Workers does not supply CF_PAGES_URL; configure the URL in the Worker's runtime variables.

OAuth Redirects Fail

Ensure your OAuth provider's authorized redirect URIs include:

  • https://your-app.com/api/auth/callback/google
  • https://your-app.com/api/auth/callback/github
  • (Replace with your domain and providers)

Include app.baseURL when your Nuxt app runs below a base path. For app.baseURL: '/app/', the Google callback is https://your-app.com/app/api/auth/callback/google; other providers use the same prefix.

DevTools

DevTools are automatically disabled in production (NODE_ENV=production). The /api/_better-auth/* endpoints and /__better-auth-devtools page are not registered.