---
title: Security
description: Better Auth security features.
---

This page contains information about security features of Better Auth.


## Password Hashing

Better Auth uses the `scrypt` algorithm to hash passwords by default. This algorithm is designed to be memory-hard and CPU-intensive, making it resistant to brute-force attacks. You can customize the password hashing function by setting the `password` option in the configuration. This option should include a `hash` function to hash passwords and a `verify` function to verify them.

## Session Management

### Session Expiration

Better Auth uses secure session management to protect user data. Sessions are stored in the database or a secondary storage, if configured, to prevent unauthorized access. By default, sessions expire after 7 days, but you can customize this value in the configuration. Additionally, each time a session is used, if it reaches the `updateAge` threshold, the expiration date is extended, which by default is set to 1 day.

### Session Revocation

Better Auth allows you to revoke sessions to enhance security. When a session is revoked, the user is logged out and can no longer access the application. A logged in user can also revoke their own sessions to log out from different devices or browsers.

See the [session management](/docs/concepts/session-management) for more details.

## CSRF Protection

Better Auth includes multiple safeguards to prevent Cross-Site Request Forgery (CSRF) attacks:

1. **Avoid simple requests**
   See [Avoiding simple requests](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF#avoiding_simple_requests) for more details. Better Auth only allows requests with a non-simple header or a `Content-Type` header of `application/json`.
   
2. **Origin Validation**
   Each request’s `Origin` header is verified to confirm it comes from your application or another explicitly trusted source. Requests from untrusted origins are rejected. By default, Better Auth trusts the base URL of your app, but you can specify additional trusted origins via the `trustedOrigins` configuration option.

3. **Secure Cookie Settings**
   Session cookies use the `SameSite=Lax` attribute by default, preventing browsers from sending cookies with most cross-site requests. You can override this behavior using the `defaultCookieAttributes` option.

4. **Fetch Metadata Protection for First-Login CSRF**
   Better Auth also uses [Fetch Metadata headers](https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF#fetch_metadata) (`Sec-Fetch-Site`, `Sec-Fetch-Mode`, `Sec-Fetch-Dest`) to provide additional CSRF protection specifically for **first-login scenarios**, where the client does not yet have any cookies.
   
   This protection applies to **sign-in and sign-up email routes**, which accept form submissions (simple requests) to support progressive enhancement patterns.
   
   When a sign-in or sign-up request is received **without** any cookies:
   
   - If the browser indicates a **cross-site navigation** (for example: `Sec-Fetch-Site: cross-site` and `Sec-Fetch-Mode: navigate`), Better Auth **blocks** the request as a potential login CSRF attack.
   
   - If the request is **same-origin** or **same-site**, Better Auth still validates the `Origin`/`Referer` against your `trustedOrigins` configuration, using the existing origin validation logic.
   
   - If the client does **not** send Fetch Metadata headers (older browsers, some mobile WebViews, non-browser clients), Better Auth falls back to its **previous behavior** and only performs origin validation when cookies are present, preserving backward compatibility.
   
   This mechanism allows modern browsers to receive stronger protection against first-login CSRF attacks **without requiring CSRF tokens or client-side JavaScript**, and works seamlessly with progressive enhancement patterns.
   
   <Callout type="info">
     Non-browser HTTP clients (mobile apps, server-to-server) should set the `Origin` or `Referer` header appropriately when making requests that include cookies. This ensures proper CSRF validation regardless of Fetch Metadata support.
   </Callout> 

5. **No Mutations on GET Requests (with additional safeguards)**
  `GET` requests are assumed to be read-only and should not alter the application's state. In cases where a `GET` request must perform a mutation—such as during OAuth callbacks - Better Auth applies extra security measures, including validating `nonce` and `state` parameters to ensure the request's authenticity.

### Disabling Security Checks

Better Auth provides two separate options to disable security checks. These options control different aspects of security:

#### `disableCSRFCheck`

Disables **all CSRF protection**, including:
- Origin header validation when cookies are present
- Fetch Metadata checks (`Sec-Fetch-Site`, `Sec-Fetch-Mode`, `Sec-Fetch-Dest`)
- Cross-site navigation blocking for first-login scenarios

```typescript
{
  advanced: {
    disableCSRFCheck: true
  }
}
```

<Callout type="warning">
  Disabling CSRF checks allows requests from any origin to use cookies and perform actions on behalf of users. This opens your application to CSRF attacks.
</Callout>

#### `disableOriginCheck`

Disables **URL validation** against `trustedOrigins`, including:
- `callbackURL` validation
- `redirectTo` validation
- `errorCallbackURL` validation
- `newUserCallbackURL` validation

```typescript
{
  advanced: {
    disableOriginCheck: true
  }
}
```

<Callout type="warning">
  Disabling origin checks allows any URL to be used in redirects and callbacks. This opens your application to open redirect vulnerabilities.
</Callout>

<Callout type="info">
  For backward compatibility, `disableOriginCheck: true` also disables CSRF protection. If you only want to disable URL validation without affecting CSRF protection, this is not currently possible - both checks are disabled together when using this option.
</Callout>

#### Summary

| Option | What it disables |
|--------|-----------------|
| `disableCSRFCheck` | CSRF protection only (origin header validation, Fetch Metadata checks) |
| `disableOriginCheck` | URL validation AND CSRF protection (for backward compatibility) |

## OAuth State and PKCE

To secure OAuth flows, Better Auth stores the OAuth state and PKCE (Proof Key for Code Exchange) in the database. The state helps prevent CSRF attacks, while PKCE protects against code injection threats. Once the OAuth process completes, these values are removed from the database.

## Cookies

Better Auth assigns secure cookies by default when the base URL uses `https`. These secure cookies are encrypted and only sent over secure connections, adding an extra layer of protection. They are also set with the `sameSite` attribute to `lax` by default to prevent cross-site request forgery attacks. And the `httpOnly` attribute is enabled to prevent client-side JavaScript from accessing the cookie. 

For Cross-Subdomain Cookies, you can set the `crossSubDomainCookies` option in the configuration. This option allows cookies to be shared across subdomains, enabling seamless authentication across multiple subdomains.

### Customizing Cookies

You can customize cookie names to minimize the risk of fingerprinting attacks and set specific cookie options as needed for additional control. For more information, refer to the [cookie options](/docs/concepts/cookies).

Plugins can also set custom cookie options to align with specific security needs. If you're using Better Auth in non-browser environments, plugins offer ways to manage cookies securely in those contexts as well.

## Rate Limiting

Better Auth includes built-in rate limiting to safeguard against brute-force attacks. Rate limits are applied across all routes by default, with specific routes subject to stricter limits based on potential risk.

## IP Address Headers

Better Auth uses client IP addresses for rate limiting and security monitoring. By default, it reads the IP address from the standard `X-Forwarded-For` header. However, you can configure a specific trusted header to ensure accurate IP address detection and prevent IP spoofing attacks.

You can configure the IP address header in your Better Auth configuration:

```typescript
{
  advanced: {
    ipAddress: {
      ipAddressHeaders: ['cf-connecting-ip'] // or any other custom header
    }
  }
}
```

This ensures that Better Auth only accepts IP addresses from your trusted proxy's header, making it more difficult for attackers to bypass rate limiting or other IP-based security measures by spoofing headers.

<Callout type="info">
**Important**

- When setting a custom IP address header, ensure that your proxy or load balancer is properly configured to set this header, and that it cannot be set by end users directly.
- In dev/test environments, if the IP cannot be retrieved from headers, 127.0.0.1 is used as a fallback.
</Callout>

## Trusted Proxy Headers

If your application runs behind a reverse proxy or load balancer, Better Auth can derive the base URL from the inbound request's `X-Forwarded-Host` and `X-Forwarded-Proto` headers. This is useful when your app is accessible from multiple domains (e.g. `example.com` and `app.example.dev`) and you don't want to hardcode a single `baseURL` in the configuration.

When `trustedProxyHeaders` is enabled and no `baseURL` is set in the configuration (or via environment variables), Better Auth uses the forwarded headers to construct the base URL on each request. This means OAuth callbacks, email verification links, and redirects will automatically use whichever domain the user loaded the app on.

```typescript
{
  advanced: {
    trustedProxyHeaders: true
  }
}
```

<Callout type="warning">
Only enable this option if you trust the proxy headers reaching your application. Your reverse proxy or load balancer must be configured to set `X-Forwarded-Host` and `X-Forwarded-Proto` correctly, and end users must not be able to set these headers directly. If untrusted clients can forge these headers, they could manipulate redirects and callback URLs.
</Callout>

When enabled, the base URL resolution follows this priority:

1. Static `baseURL` from the configuration (if set, proxy headers are ignored)
2. Environment variables (`BETTER_AUTH_URL`, `NEXT_PUBLIC_BETTER_AUTH_URL`, etc.)
3. `X-Forwarded-Host` + `X-Forwarded-Proto` headers (when `trustedProxyHeaders` is `true`)
4. The request URL origin as a final fallback

If you're serving your app from multiple approved domains, you'll typically want to:
- **Omit `baseURL`** from the configuration so it's derived per-request from the proxy headers
- Set **`trustedOrigins`** to an allowlist of your approved domains (see below)
- Leave `crossSubDomainCookies` disabled — cookies are host-only by default, which means each domain gets its own independent session

## Trusted Origins

Trusted origins prevent CSRF attacks and block open redirects. You can set a list of trusted origins in the `trustedOrigins` configuration option. Requests from origins not on this list are automatically blocked.

### Basic Usage

The most basic usage is to specify exact origins:

```typescript
{
  trustedOrigins: [
    "https://example.com",
    "https://app.example.com",
    "http://localhost:3000"
  ]
}
```

### Wildcard Origins

Better Auth supports wildcard patterns in trusted origins, which allows you to trust multiple subdomains with a single entry:

```typescript
{
  trustedOrigins: [
    "*.example.com",             // Trust all subdomains of example.com (any protocol)
    "https://*.example.com",     // Trust only HTTPS subdomains of example.com
    "http://*.dev.example.com"   // Trust all HTTP subdomains of dev.example.com
  ]
}
```

#### Protocol-specific wildcards

When using a wildcard pattern with a protocol prefix (like `https://`):
- The protocol must match exactly
- The domain can have any subdomain in place of the `*`
- Requests using a different protocol will be rejected, even if the domain matches

#### Protocol-agnostic wildcards

When using a wildcard pattern without a protocol prefix (like `*.example.com`):
- Any protocol (http, https, etc.) will be accepted
- The domain must match the wildcard pattern

### Custom Schemes

Trusted origins also support custom schemes for mobile apps and browser extensions:

```typescript
{
  trustedOrigins: [
    "myapp://",                               // Mobile app scheme
    "chrome-extension://YOUR_EXTENSION_ID",   // Browser extension
    "exp://**",                               // Trust all Expo development URLs
    "exp://10.0.0.*:*/**",                    // Trust 10.0.0.x IP range with any port
  ]
}
```

### Dynamic origin list

You can also dynamically set the list of trusted origins by providing a function that returns it:

```typescript
{
  trustedOrigins: async (request) => {
    const trustedOrigins = await queryTrustedDomains();
    return trustedOrigins;
  }
}
```

<Callout>
**Important**: This function will be invoked per incoming request, so be careful if you decide to dynamically fetch your list of trusted domains.
</Callout>

## Reporting Vulnerabilities

If you discover a security vulnerability in Better Auth, please report it to us at [security@better-auth.com](mailto:security@better-auth.com). We address all reports promptly, and credits will be given for validated discoveries.
