-
Notifications
You must be signed in to change notification settings - Fork 0
Rate Limiting
The API limits how often each client can call it — to absorb traffic bursts and block abuse such as password-guessing on the login endpoint. It uses a token-bucket limiter implemented as a WebFilter that runs before Spring Security, so excess traffic is rejected before any authentication or business logic runs.
Each client has a bucket that holds up to N tokens:
- Every request takes one token.
- Tokens refill continuously at a fixed rate (N tokens per period).
- If a token is available, the request is allowed; if the bucket is empty, the request is rejected.
This permits short bursts (up to the bucket's capacity) while capping the sustained rate to the refill rate.
flowchart TD
R["Request"] --> X{"Excluded path? (docs, health)"}
X -->|Yes| P["Pass through"]
X -->|No| K["Build key: tier + client IP"]
K --> C{"Token available?"}
C -->|Yes| A["Consume token, allow, add X-RateLimit-Remaining"]
C -->|No| D["Reject with 429 + Retry-After"]
The limit depends on the endpoint:
| Tier | Applies to | Limit |
|---|---|---|
| auth (strict) | /api/auth/** |
~10 / minute — slows brute-force and credential stuffing |
| general | everything else | ~100 / minute |
Documentation and health endpoints (/v3/api-docs, /swagger-ui, /webjars, /actuator) are excluded entirely.
The bucket key is tier + client IP, so each client is limited independently — one noisy client cannot exhaust everyone else's allowance.
The client IP comes from the connection's remote address. Behind a trusted proxy you can set ratelimit.trust-forwarded-for=true to use the first X-Forwarded-For entry instead — only enable this when a known proxy sets that header, because clients can otherwise spoof it to dodge the limit.
RateLimiter is pluggable:
- in-memory — for a single instance (local / dev).
- Redis — shared across instances; the check-refill-consume step runs atomically (a Lua script) so every instance agrees on the count.
Selected with RATELIMIT_BACKEND=memory|redis (deployed profiles use Redis).
-
Allowed → the request proceeds; every response carries an
X-RateLimit-Remainingheader. -
Exceeded →
429 Too Many Requestswith an RFC 7807application/problem+jsonbody and aRetry-Afterheader telling the client when to retry. - Backend unavailable → the filter fails open (allows the request), so a broken limiter does not take the whole API down.
| Setting | Purpose | Default |
|---|---|---|
RATELIMIT_ENABLED |
turn the limiter on/off | true |
RATELIMIT_BACKEND |
memory or redis
|
memory (local) |
RATELIMIT_TRUST_XFF |
trust X-Forwarded-For for the client IP |
false |
ratelimit.auth / ratelimit.general
|
per-tier capacity + refill period | 10 / min, 100 / min |
See Architecture for how this fits into the overall request flow.
Getting started
How it works
Operations
Reference