Rate limit does not work when IP is unknown #1198

Closed
opened 2026-03-13 08:27:45 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @vladshcherbin on GitHub (May 13, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

localhost

  1. create a simple app (in my case with sveltekit)
  2. run dev server (in my case vite dev)
  3. add auth with rate limit config (in my case with OTP)
  4. execute as many api calls as you want (in my case emailOtp.sendVerificationOtp)

Current vs. Expected behavior

I've tried to use rate limit, set enabled to true, tried different configurations - all with no luck.

After debugging I found out it's not working if IP is unknown - https://github.com/better-auth/better-auth/blob/v1.2.7/packages/better-auth/src/api/rate-limiter/index.ts#L122

This was on localhost, sveltekit, vite dev server.

There is also test ip check but it fails due to NODE_ENV being not test but development - https://github.com/better-auth/better-auth/blob/v1.2.7/packages/better-auth/src/utils/get-request-ip.ts#L12

Rate limit docs say nothing about IP necessity and that if IP is not resolved, rate limit doesn't work.

There must be a note about this one. Same for disabling IP tracking option - if you disable it, you also disable rate limits.

Ideally on localhost some IP would also be used for rate limit to work.

🤯

What version of Better Auth are you using?

1.2.7

Provide environment information

- OS: mac 15
- Browser: Chrome
- Node 24, SvelteKit

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

Backend, Client, Documentation

Auth config (if applicable)

import { betterAuth } from "better-auth"
import { emailOTP } from "better-auth/plugins"
import database from "./database.ts"

export const auth = betterAuth({
  advanced: {
    database: {
      useNumberId: true
    }
  },
  database: {
    db: database,
    type: 'postgres'
  },
  plugins: [
    emailOTP({
      async sendVerificationOTP({ email, otp, type }) {
        // ...
      }
    })
  ],
  rateLimit: {
    enabled: true,
    max: 1,
    window: 60
  }
});

Additional context

No response

Originally created by @vladshcherbin on GitHub (May 13, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce localhost 1. create a simple app (in my case with sveltekit) 2. run dev server (in my case vite dev) 3. add auth with rate limit config (in my case with OTP) 4. execute as many api calls as you want (in my case `emailOtp.sendVerificationOtp`) ### Current vs. Expected behavior I've tried to use rate limit, set `enabled` to `true`, tried different configurations - all with no luck. After debugging I found out it's not working if IP is unknown - https://github.com/better-auth/better-auth/blob/v1.2.7/packages/better-auth/src/api/rate-limiter/index.ts#L122 This was on localhost, sveltekit, vite dev server. There is also test ip check but it fails due to `NODE_ENV` being not `test` but `development` - https://github.com/better-auth/better-auth/blob/v1.2.7/packages/better-auth/src/utils/get-request-ip.ts#L12 [Rate limit](https://www.better-auth.com/docs/concepts/rate-limit) docs say nothing about IP necessity and that if IP is not resolved, rate limit doesn't work. There must be a note about this one. Same for disabling IP tracking option - if you disable it, you also disable rate limits. Ideally on localhost some IP would also be used for rate limit to work. 🤯 ### What version of Better Auth are you using? 1.2.7 ### Provide environment information ```bash - OS: mac 15 - Browser: Chrome - Node 24, SvelteKit ``` ### Which area(s) are affected? (Select all that apply) Backend, Client, Documentation ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" import { emailOTP } from "better-auth/plugins" import database from "./database.ts" export const auth = betterAuth({ advanced: { database: { useNumberId: true } }, database: { db: database, type: 'postgres' }, plugins: [ emailOTP({ async sendVerificationOTP({ email, otp, type }) { // ... } }) ], rateLimit: { enabled: true, max: 1, window: 60 } }); ``` ### Additional context _No response_
Author
Owner

@DiiiaZoTe commented on GitHub (May 13, 2025):

I was able to fix it with this PR, but it has not been merged yet.
https://github.com/better-auth/better-auth/pull/2511

Basically if you use NODE_ENV as development, then it does not apply a default local ip 127.0.0.1. It does it for NODE_ENV = test. Thus, when it goes for the ip check when checking rate limit, it fails because undefined is not a valid ip.

For now a fix is to add a simple middleware to your backend (here hono). Replace config.IP_ADDRESS_HEADER with whatever name you like for the header.

.use("*", (c, next) => {
    // we will assume the local ip address of 127.0.0.1 if we don't have a valid ip address
    let ip = getConnInfo(c).remote.address ?? "127.0.0.1";
    if (ip === "::1" || !ip) ip = "127.0.0.1"; // for local development
    c.req.raw.headers.set(config.IP_ADDRESS_HEADER, ip);
    return next();
})

Then in the auth config add:

advanced: {
    ipAddress: {
      ipAddressHeaders: [config.IP_ADDRESS_HEADER, "x-forwarded-for", "x-real-ip"],
      disableIpTracking: false
    },
  },
@DiiiaZoTe commented on GitHub (May 13, 2025): I was able to fix it with this PR, but it has not been merged yet. https://github.com/better-auth/better-auth/pull/2511 Basically if you use NODE_ENV as development, then it does not apply a default local ip 127.0.0.1. It does it for `NODE_ENV = test`. Thus, when it goes for the ip check when checking rate limit, it fails because undefined is not a valid ip. For now a fix is to add a simple middleware to your backend (here hono). Replace `config.IP_ADDRESS_HEADER` with whatever name you like for the header. ``` .use("*", (c, next) => { // we will assume the local ip address of 127.0.0.1 if we don't have a valid ip address let ip = getConnInfo(c).remote.address ?? "127.0.0.1"; if (ip === "::1" || !ip) ip = "127.0.0.1"; // for local development c.req.raw.headers.set(config.IP_ADDRESS_HEADER, ip); return next(); }) ``` Then in the auth config add: ``` advanced: { ipAddress: { ipAddressHeaders: [config.IP_ADDRESS_HEADER, "x-forwarded-for", "x-real-ip"], disableIpTracking: false }, }, ```
Author
Owner

@dosubot[bot] commented on GitHub (Aug 12, 2025):

Hi, @vladshcherbin. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You reported that rate limiting fails when the client's IP is unknown, especially during local development with SvelteKit and Vite.
  • IP detection does not work on localhost, causing the issue.
  • A contributor shared a middleware workaround to set a default local IP (127.0.0.1) during development.
  • An unmerged PR was referenced that addresses this IP handling limitation.
  • The workaround and PR also suggest updating documentation to clarify this behavior.

Next Steps:

  • Please let me know if this issue is still relevant with the latest version of better-auth by commenting here.
  • If I don’t hear back within 7 days, I will automatically close this issue.

Thanks for your understanding and contribution!

@dosubot[bot] commented on GitHub (Aug 12, 2025): Hi, @vladshcherbin. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary:** - You reported that rate limiting fails when the client's IP is unknown, especially during local development with SvelteKit and Vite. - IP detection does not work on localhost, causing the issue. - A contributor shared a middleware workaround to set a default local IP (127.0.0.1) during development. - An unmerged PR was referenced that addresses this IP handling limitation. - The workaround and PR also suggest updating documentation to clarify this behavior. **Next Steps:** - Please let me know if this issue is still relevant with the latest version of better-auth by commenting here. - If I don’t hear back within 7 days, I will automatically close this issue. Thanks for your understanding and contribution!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1198