Rate limiting applies globally (cannot restrict to specific routes) #1872

Closed
opened 2026-03-13 09:09:54 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @developedbyed on GitHub (Sep 7, 2025).

Is this suited for github?

  • Yes, this is suited for github

Description

When enabling rateLimit in Better Auth, it currently applies globally to all routes (e.g., /get-session).
This results in unnecessary keys being created in secondary storage (see screenshot below).

I would like to restrict rate limiting to only certain routes (e.g., /callback/google or /sign-in/social). While customRules lets me override the window/max for a given route, it doesn’t allow me to fully opt-out of rate limiting on other routes.

To Reproduce

Configure Better Auth with secondary storage (Cloudflare KV in my case).

Enable rateLimit with secondary storage.

Add a custom rule for /sign-in/social.

Make requests against /get-session.

Result

Rate limiting is still applied to /get-session and keys are created in storage, even though I only want it on my social login routes.

Current vs. Expected behavior

Current: Rate limiting is applied to all routes by default, even ones I don’t want to rate-limit (e.g., /get-session).

Expected: Ability to apply rate limiting only to selected routes. For example, only /sign-in/social and /callback/google, while excluding /get-session.

What version of Better Auth are you using?

What version of Better Auth are you using?

1.3.7

System info

-

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

### Auth config

import { betterAuth } from "better-auth"
import { drizzleAdapter } from "better-auth/adapters/drizzle"
import { db } from "~/db"
import { accounts, users, verifications } from "~/db/schema"
import { getCloudflareCtx } from "../cloudflare"

export const auth = betterAuth({
  session: {
    expiresIn: 60 * 60 * 24 * 7,
    updateAge: 60 * 60 * 24,
  },
  advanced: {
    ipAddress: {
      ipAddressHeaders: ["cf-connecting-ip"],
    },
  },
  rateLimit: {
    enabled: true,
    window: 60,
    max: 100,
    storage: "secondary-storage",
    modelName: "rateLimit",
    customRules: {
      "/sign-in/social": async request => {
        return {
          window: 60,
          max: 6,
        }
      },
    },
  },
  database: drizzleAdapter(db, {
    provider: "pg",
    schema: {
      verification: verifications,
      account: accounts,
      user: users,
    },
  }),
  user: {
    additionalFields: {
      role: {
        type: "string",
        fieldName: "role",
      },
      points: {
        type: "number",
        fieldName: "points",
      },
    },
  },
  secondaryStorage: {
    get: async key => {
      const cf = await getCloudflareCtx()
      const value = await cf.KV_PLATFORM.get(key)
      return value ? value : null
    },
    set: async (key, value, ttl) => {
      const cf = await getCloudflareCtx()
      if (ttl) await cf.KV_PLATFORM.put(key, value, { expirationTtl: ttl })
      else await cf.KV_PLATFORM.put(key, value)
    },
    delete: async key => {
      const cf = await getCloudflareCtx()
      await cf.KV_PLATFORM.delete(key)
    },
  },
  secret: process.env.BETTER_AUTH_SECRET!,
  baseURL: process.env.BETTER_AUTH_URL!,
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    },
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    },
  },
})

Additional context

No response

Originally created by @developedbyed on GitHub (Sep 7, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### Description When enabling rateLimit in Better Auth, it currently applies globally to all routes (e.g., /get-session). This results in unnecessary keys being created in secondary storage (see screenshot below). I would like to restrict rate limiting to only certain routes (e.g., /callback/google or /sign-in/social). While customRules lets me override the window/max for a given route, it doesn’t allow me to fully opt-out of rate limiting on other routes. ### To Reproduce Configure Better Auth with secondary storage (Cloudflare KV in my case). Enable rateLimit with secondary storage. Add a custom rule for /sign-in/social. Make requests against /get-session. ### Result Rate limiting is still applied to /get-session and keys are created in storage, even though I only want it on my social login routes. ### Current vs. Expected behavior Current: Rate limiting is applied to all routes by default, even ones I don’t want to rate-limit (e.g., /get-session). Expected: Ability to apply rate limiting only to selected routes. For example, only /sign-in/social and /callback/google, while excluding /get-session. What version of Better Auth are you using? ### What version of Better Auth are you using? 1.3.7 ### System info ```bash - ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript ### Auth config import { betterAuth } from "better-auth" import { drizzleAdapter } from "better-auth/adapters/drizzle" import { db } from "~/db" import { accounts, users, verifications } from "~/db/schema" import { getCloudflareCtx } from "../cloudflare" export const auth = betterAuth({ session: { expiresIn: 60 * 60 * 24 * 7, updateAge: 60 * 60 * 24, }, advanced: { ipAddress: { ipAddressHeaders: ["cf-connecting-ip"], }, }, rateLimit: { enabled: true, window: 60, max: 100, storage: "secondary-storage", modelName: "rateLimit", customRules: { "/sign-in/social": async request => { return { window: 60, max: 6, } }, }, }, database: drizzleAdapter(db, { provider: "pg", schema: { verification: verifications, account: accounts, user: users, }, }), user: { additionalFields: { role: { type: "string", fieldName: "role", }, points: { type: "number", fieldName: "points", }, }, }, secondaryStorage: { get: async key => { const cf = await getCloudflareCtx() const value = await cf.KV_PLATFORM.get(key) return value ? value : null }, set: async (key, value, ttl) => { const cf = await getCloudflareCtx() if (ttl) await cf.KV_PLATFORM.put(key, value, { expirationTtl: ttl }) else await cf.KV_PLATFORM.put(key, value) }, delete: async key => { const cf = await getCloudflareCtx() await cf.KV_PLATFORM.delete(key) }, }, secret: process.env.BETTER_AUTH_SECRET!, baseURL: process.env.BETTER_AUTH_URL!, socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, }, google: { clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, }, }, }) ``` ### Additional context _No response_
GiteaMirror added the bug label 2026-03-13 09:09:54 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Sep 7, 2025):

Hi @developedbyed! I'm Dosu and I’m helping the better-auth team.

Better Auth's rate limiting currently applies globally to all routes when enabled. The customRules option lets you customize the window and max for specific routes, but it does not allow you to fully opt out of rate limiting for other routes using configuration alone. There is no built-in way to restrict rate limiting to only selected routes via config in v1.3.7—this matches your observations.

To achieve per-route rate limiting (e.g., only rate limit /sign-in/social and /callback/google), you need to use the customStorage option. With customStorage, you can implement logic in the get and set methods to apply rate limiting only for the routes you want, and skip it for others. For example, you can check the request URL and only store/retrieve rate limit data for your chosen endpoints, returning undefined for all others so they are not rate limited or tracked in storage. This pattern is described in this discussion.

There are no recent changes or merged pull requests that add a configuration-only way to opt out of rate limiting for specific routes or to restrict it to selected routes only1 2 .

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 7, 2025): <!-- Greeting --> Hi @developedbyed! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> Better Auth's rate limiting currently applies globally to all routes when enabled. The `customRules` option lets you customize the window and max for specific routes, but it does not allow you to fully opt out of rate limiting for other routes using configuration alone. There is no built-in way to restrict rate limiting to only selected routes via config in v1.3.7—this matches your observations. To achieve per-route rate limiting (e.g., only rate limit `/sign-in/social` and `/callback/google`), you need to use the `customStorage` option. With `customStorage`, you can implement logic in the `get` and `set` methods to apply rate limiting only for the routes you want, and skip it for others. For example, you can check the request URL and only store/retrieve rate limit data for your chosen endpoints, returning `undefined` for all others so they are not rate limited or tracked in storage. This pattern is described in [this discussion](https://github.com/better-auth/better-auth/discussions/3865). There are no recent changes or merged pull requests that add a configuration-only way to opt out of rate limiting for specific routes or to restrict it to selected routes only[^1][^2]. [^1]: [Better Auth types/options.ts](https://github.com/better-auth/better-auth/blob/a39d09d3ff83b526fb9caf691d3bbcb3b7b2335b/packages/better-auth/src/types/options.ts) [^2]: [Discussion on customStorage for per-route rate limiting](https://github.com/better-auth/better-auth/discussions/3865) <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/411bb86c-5066-48ed-a128-0ef423a8af21?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/411bb86c-5066-48ed-a128-0ef423a8af21?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/411bb86c-5066-48ed-a128-0ef423a8af21?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/411bb86c-5066-48ed-a128-0ef423a8af21?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/411bb86c-5066-48ed-a128-0ef423a8af21?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/411bb86c-5066-48ed-a128-0ef423a8af21?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/411bb86c-5066-48ed-a128-0ef423a8af21?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4497)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1872