Skip to content

Commit 1cb95de

Browse files
Brooooooklynclaude
andcommitted
Explain auth rate limits behind a proxy and in end-to-end tests
Deployed Cloudflare apps now apply Better Auth's default rate limits per client IP. A server that calls auth for many users shares one IP, and tests that sign in often hit the sign-in limit. Show a secret-header rule that skips the limits only for a trusted caller, and say that a proxy using it must limit the requests it forwards. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent f49a56a commit 1cb95de

1 file changed

Lines changed: 32 additions & 0 deletions

File tree

‎docs/guide/auth.md‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,38 @@ Local development needs no secret setup. To preview a production build locally,
131131

132132
Deployed apps on Cloudflare limit auth requests per client IP with Better Auth's defaults, for example 3 sign-in attempts per 10 seconds. Each Worker instance keeps its own count. Change or turn off the limits with `rateLimit` in `auth.ts`. To share one count across instances, set `rateLimit: { storage: 'database' }`, then run `void db generate` for a direct Cloudflare deploy.
133133

134+
### Proxies and end-to-end tests
135+
136+
All requests from one IP share one count. Two common setups reach the limits:
137+
138+
- A server calls your auth endpoints for your users, such as a backend-for-frontend, a rewrite from Vercel or nginx, or another Worker's `fetch`. All of those users share that server's IP.
139+
- End-to-end tests sign up or sign in many times against a deployed app.
140+
141+
Don't read the client IP from `x-forwarded-for` instead: any client can send that header. Give the trusted caller a secret header, and skip the limits only for requests that carry it:
142+
143+
```ts
144+
import { defineAuth } from 'void/auth';
145+
146+
export default defineAuth(({ defaults, env }) => ({
147+
...defaults,
148+
rateLimit: {
149+
customRules: {
150+
'/**': (request, rule) =>
151+
env.AUTH_TRUSTED_KEY && request.headers.get('x-auth-trusted-key') === env.AUTH_TRUSTED_KEY
152+
? false
153+
: rule,
154+
},
155+
},
156+
}));
157+
```
158+
159+
`'/**'` matches every auth endpoint. Returning `false` skips the limit, and returning `rule` keeps the default limit for everyone else. Declare `AUTH_TRUSTED_KEY: string().optional()` in `env.ts` and set it with `void secret put AUTH_TRUSTED_KEY` (see [Environment variables](./env-vars.md#production-server-values)).
160+
161+
Your app no longer limits requests that carry the key, so:
162+
163+
- A proxy that sends it must limit every auth request it forwards for each of its users, including sign-up, password reset and verification emails.
164+
- A test runner should send it only to your app. Never put the key in code that runs in your users' browsers.
165+
134166
## Customization
135167

136168
For advanced configuration, create `auth.ts` at the project root and export `defineAuth(...)`:

0 commit comments

Comments
 (0)