-
Notifications
You must be signed in to change notification settings - Fork 0
Troubleshooting
TrustedProxies is not configured and something sits in front of the
application. RemoteAddr is then the proxy's address — identical for every
client — so the per-client limiter is a single global one.
ratelimit.WithTrustedProxies(ratelimit.PrivateNetworks()...)This is the most common misconfiguration, and it is silent: the limiter fires, just for everyone at once.
You are trusting a hop you should not, or an implementation is reading
X-Forwarded-For from the left. Test it:
curl -si -H 'X-Forwarded-For: 1.2.3.4' https://api.example.com/health | grep -i x-ratelimitIf X-RateLimit-Remaining resets, the client-supplied value is being used as
the key. Narrow TrustedProxies to the actual ingress subnet.
The correct walk is right to left, skipping trusted hops — because a proxy appends, so the leftmost entry is whatever the client sent. See Behind a Proxy.
-
Pointer receiver, value registration.
func (r *LoginRoute) RateLimit()needsapp.RegisterRoute(&LoginRoute{…}). -
The route implements
RateLimitByContexttoo — the dynamic variant takes precedence. -
You are on an old version. Per-endpoint limits used to be resolved per
request from the live URL against an index keyed by the route's pattern, so
they silently never applied to any route with a path parameter. If
/users/{id}ignores its limit but/healthrespects one, that is the symptom.
Older versions hardcoded the default router's name when resolving the chain, so
a limit configured for any other router was never consulted. Current versions
receive the router name in RouteInfo.
Also check the name matches exactly — WithRouterLimit("partner", …) against a
router created as "partner-api" matches nothing, silently.
- Look at the level. A global limit that fires on normal aggregate traffic is set too low; it is meant as a backstop.
- Check whether every client is sharing a bucket — see the first entry.
- Check the window. A sliding window smooths bursts, but a client that legitimately bursts (a page load issuing twenty parallel requests) needs a budget that accommodates the burst, not the average.
- No limit is configured at any level, and no route declares one.
- The extension is registered but
WithRateLimit()was called with no options.
Check the headers — X-RateLimit-Limit is absent when no limit applies to the
route.
They must be exposed through CORS. rextension-cors does that by default,
but WithExposedHeaders replaces the defaults — if you set it, list them again:
cors.WithExposedHeaders(
"X-RateLimit-Limit", "X-RateLimit-Remaining", "X-RateLimit-Reset", "Retry-After",
)The bucket store is filling. Check:
- Is the key function unbounded? Anything client-supplied and unhashed makes the store's size the client's choice.
-
Is
TrustedProxiestoo wide? Trusting an untrusted hop makesX-Forwarded-Forthe key, which a client can rotate freely. -
Has
MaxBucketsbeen disabled? A negative value removes the cap.
Idle eviction alone does not bound the store: buckets are removed after ten minutes idle, evaluated every five, so a client creating them faster than that holds (creation rate × 10 minutes) buckets indefinitely.
They are per process. Two replicas mean each client gets two budgets. The extension has no shared store — rate limit at the ingress if you need a global budget, or divide the intended limit by the replica count and accept that it is approximate during a rolling deploy.
MustParsePrefixes panics on a malformed prefix, on purpose: silently producing
an empty trust list would look like working code behind a proxy while actually
rate limiting every client as one.
For prefixes from configuration, use netip.ParsePrefix and return the error.
It is called on every request. Read values out of the context; do not do I/O. If the tier lookup needs a database, cache it on the principal at authentication time and read it from the context here.
It should not — PriorityRateLimit is 300 and PriorityAuth is 400, so the
limiter is outside. If it is not, something registered the middleware by hand at
a different priority. Rate limiting after authentication means every flooded
request costs a token verification before being rejected.
rextension-ratelimit — global, per-router and per-endpoint rate limiting for Rex · MIT · © 2026 Kryovyx
Ecosystem