Core Concepts

Security & Caveats

What is enforced where, and what you should not assume.

Use this page when you are moving from “it works locally” to “it is safe to deploy”.

Client redirects are not security

Client-side redirects improve UX but don't prevent unauthorized access. Always validate on the server.

CSRF / origin checks

Better Auth performs origin checks for cookie-based requests (it validates Origin / Referer). If you use multiple domains (custom domains, preview URLs, etc.), add them to trustedOrigins in your auth config.

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

export default defineServerAuth({
  trustedOrigins: [
    'http://localhost:3000',
    'https://your-domain.com',
    'https://your-preview.workers.dev',
  ],
})
If you deploy preview environments, you must include the preview origin in trustedOrigins. Otherwise, sign-in or sign-up requests can fail origin validation even when the API endpoint is healthy.

API enforcement behavior

The built-in Nitro middleware checks routeRules.auth for app-owned /api/** routes. It skips Better Auth's /api/auth/** handler and known framework or module internals such as /api/_nuxt_icon/** and /api/_better-auth/**, so broad route rules do not break auth, icons, or module tooling.

Customizing Unauthorized Behavior

By default, unauthorized requests to protected routes receive a 401 response. To customize:

Redirect to login instead of 401:

server/middleware/auth.ts
import { sendRedirect } from 'h3'

export default defineEventHandler(async (event) => {
  const session = await getUserSession(event)
  if (!session && event.path.startsWith('/api/protected')) {
    return sendRedirect(event, '/login', 302)
  }
})

Return JSON error for API routes:

// This is the default behavior for /api/* routes
throw createError({
  statusCode: 401,
  data: { error: 'Authentication required' }
})

If you want different behavior (e.g. enforce auth: 'user' for APIs), add your own Nitro middleware and/or call requireUserSession(event) directly inside handlers.

server/api/secret.get.ts
export default defineEventHandler(async (event) => {
  await requireUserSession(event)
  return { ok: true }
})

Default login route

Unauthenticated users are redirected to /login when a page requires auth. To use another path, set redirectTo on the protected route rule (or page meta auth object).

If your redirectTo target does not exist, users will be redirected to a 404 page. Ensure redirect targets are valid app routes.

Rate limiting

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

Rate-limit data is stored in memory by default. For serverless or multi-instance deployments, configure Better Auth to use database, secondary storage, or custom storage so instances do not rely on separate in-memory counters.

DevTools in production

DevTools are automatically disabled when NODE_ENV=production. The /api/_better-auth/* endpoints and devtools page are never registered in production builds.