> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/joey727/Phisherman/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate limiting

> How Phisherman throttles requests per IP address using Redis.

Phisherman enforces a per-IP rate limit on all requests using a Redis-backed counter. The limit is applied by the `apiLimiter` middleware registered in `src/index.ts` before any route handler runs.

## Limit parameters

| Parameter           | Value                           |
| ------------------- | ------------------------------- |
| Requests per window | 100                             |
| Window duration     | 15 minutes (900 seconds)        |
| Scope               | Per client IP address           |
| Storage             | Redis String (`ratelimit:<ip>`) |

## How it works

On every incoming request, the middleware:

1. Resolves the client IP from `req.ip`, then falls back to the `x-forwarded-for` header, then `req.socket.remoteAddress`.
2. Increments the Redis key `ratelimit:<ip>` using `INCR`.
3. If this is the first request (counter equals `1`), sets a 900-second expiry on the key so the window resets automatically.
4. If the counter exceeds `100`, returns a `429` response immediately without calling the next middleware.
5. Otherwise, calls `next()` to proceed with the request.

## Middleware source

```typescript theme={null}
export const apiLimiter = async (req: Request, res: Response, next: NextFunction) => {
  const ip = req.ip || (req.headers["x-forwarded-for"] as string) || req.socket.remoteAddress;
  const key = `ratelimit:${ip}`;

  try {
    const requests = await redis.incr(key);

    if (requests === 1) {
      await redis.expire(key, 900);
    }

    if (requests > 100) {
      return res.status(429).json({
        error: "Too many requests from this IP, please try again later.",
        limit: 100,
        current: requests
      });
    }

    next();
  } catch (err) {
    console.error("Rate limit check failed:", err);
    next();
  }
};
```

## 429 response format

When the limit is exceeded, the server responds with HTTP `429` and the following JSON body:

```json theme={null}
{
  "error": "Too many requests from this IP, please try again later.",
  "limit": 100,
  "current": 101
}
```

`current` reflects the actual request count at the time of rejection, which may be higher than `limit + 1` if requests arrive concurrently.

## Reverse proxy support

Phisherman calls `app.set("trust proxy", 1)` in `src/index.ts`, which instructs Express to trust the first value in the `X-Forwarded-For` header as the real client IP. This means rate limiting correctly identifies the originating client when Phisherman is deployed behind a single reverse proxy (nginx, Caddy, a load balancer, etc.).

If you run Phisherman behind more than one proxy hop, adjust the trust proxy setting accordingly. See the [Express behind proxies](https://expressjs.com/en/guide/behind-proxies.html) documentation for details.

<Note>
  `app.set("trust proxy", 1)` is set in `src/index.ts:12`. Without this setting, `req.ip` would always be the proxy's IP address, and all traffic would share a single rate limit bucket.
</Note>

<Warning>
  If Redis is unavailable, the `catch` block calls `next()` instead of returning an error. This means the rate limit **fails open** — requests are not blocked when Redis is down. Design your deployment accordingly if you rely on rate limiting for abuse prevention.
</Warning>

<Tip>
  The rate limit is per IP address, not per API key. Phisherman has no authentication layer, so all clients connecting from the same IP share the same 100-request budget. If you need per-client quotas, you will need to add an authentication middleware and adapt the key strategy in `src/middleware/ratelimit.ts`.
</Tip>
