Production Deployment
Environment Variables
Production requires these environment variables:
# Required: 32+ character secret for session encryption
NUXT_BETTER_AUTH_SECRET="your-32-character-secret-here-minimum"
# Optional: Auto-detected on Vercel/Cloudflare/Netlify
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'))"
The secret must be at least 32 characters. Shorter secrets will cause the module to throw an error.
Security Checklist
Before Deploying
-
NUXT_BETTER_AUTH_SECRETis set and 32+ characters -
NUXT_PUBLIC_SITE_URLset (or using Vercel/Cloudflare/Netlify auto-detection) -
trustedOriginsincludes every active frontend origin (primary domain and preview domain, if used) - OAuth redirect URIs configured for production domain
-
NODE_ENV=productionis set (disables devtools)
Route Protection
Route rules and definePageMeta are for UX (redirects). Always protect API endpoints with requireUserSession:
export default defineEventHandler(async (event) => {
const { user } = await requireUserSession(event)
return { data: 'protected' }
})
Rate Limiting
The module does not include built-in rate limiting. Implement rate limiting at the infrastructure level or use a middleware:
import { getRequestIP } from 'h3'
const requests = new Map<string, number[]>()
const WINDOW_MS = 60_000 // 1 minute
const MAX_REQUESTS = 100
export default defineEventHandler((event) => {
if (!event.path.startsWith('/api/auth'))
return
const ip = getRequestIP(event) || 'unknown'
const now = Date.now()
const windowStart = now - WINDOW_MS
const timestamps = (requests.get(ip) || []).filter(t => t > windowStart)
timestamps.push(now)
requests.set(ip, timestamps)
if (timestamps.length > MAX_REQUESTS) {
throw createError({ statusCode: 429, message: 'Too many requests' })
}
})
For production, consider using Cloudflare rate limiting or a Redis-backed solution.
Trusted Origins for Preview Environments
If your workflow uses preview URLs (for example, *.workers.dev), include those origins in your Better Auth server config.
import { defineServerAuth } from '@onmax/nuxt-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:
- Database migrations run automatically during build
- Set environment variables in your deployment platform
- Ensure
@nuxthub/coreis listed before@onmax/nuxt-better-authin modules
export default defineNuxtConfig({
modules: [
'@nuxthub/core', // Must be first
'@onmax/nuxt-better-auth',
],
})
Common Issues
"NUXT_BETTER_AUTH_SECRET must be at least 32 characters"
Your secret is too short. Generate a new one using the command above. BETTER_AUTH_SECRET is still accepted as a fallback, but NUXT_BETTER_AUTH_SECRET is the recommended variable.
"siteUrl required in production"
The module auto-detects URLs on Vercel, Cloudflare Pages, and Netlify. For other platforms, set NUXT_PUBLIC_SITE_URL to your production domain.
OAuth Redirects Fail
Ensure your OAuth provider's authorized redirect URIs include:
https://your-app.com/api/auth/callback/googlehttps://your-app.com/api/auth/callback/github- (Replace with your domain and providers)
DevTools
DevTools are automatically disabled in production (NODE_ENV=production). The /api/_better-auth/* endpoints and /__better-auth-devtools page are not registered.