-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration
Unlike the other extensions, this one takes functional options directly
rather than a *Config:
app := rex.New(ratelimit.WithRateLimit(
ratelimit.WithGlobalLimit(600, time.Minute),
ratelimit.WithRouterLimit("partner", 100, time.Minute),
ratelimit.WithTrustedProxies(ratelimit.PrivateNetworks()...),
ratelimit.WithMaxBuckets(100_000),
))ratelimit.WithRateLimit() with no options attaches the extension with no
limits configured — every route is unlimited unless it declares its own.
WithGlobalLimit(rate, window) |
a fixed limit across every router |
WithGlobalLimitFunc(fn) |
a dynamic global limit |
WithRouterLimit(name, rate, window) |
a fixed limit for one router |
WithRouterLimitFunc(name, fn) |
a dynamic limit for one router |
WithKeyFunc(kf) |
override client identification |
WithTrustedProxies(prefixes…) |
networks whose X-Forwarded-For is believed |
WithMaxBuckets(n) |
cap distinct buckets; 0 → 100,000; negative → no cap |
type Config struct {
Global LimitProvider
Routers map[string]LimitProvider
KeyFunc KeyFunc // default RemoteIPKeyFunc()
TrustedProxies []netip.Prefix // empty trusts nothing
MaxBuckets int // 0 → DefaultMaxBuckets
}WithTrustedProxies also switches the key function to the forwarding-aware
one, so you do not set both.
ratelimit.PrivateNetworks() // RFC 1918, RFC 4193, loopback
ratelimit.MustParsePrefixes("10.0.0.0/8", "10.1.0.0/16")MustParsePrefixes panics on a malformed CIDR — for configuration written as a
literal, where a bad prefix is a programming error. The alternative, silently
producing an empty trust list, would look like working code behind a proxy while
actually rate limiting every client as one.
For prefixes read from configuration, parse with netip.ParsePrefix yourself
and fail startup on the error.
app := rex.New(ratelimit.WithRateLimit(
// A backstop well above normal aggregate traffic.
ratelimit.WithGlobalLimit(6_000, time.Minute),
// Audience-shaped budgets.
ratelimit.WithRouterLimit("partner", 600, time.Minute),
// The ingress subnet, not the whole private range.
ratelimit.WithTrustedProxies(ratelimit.MustParsePrefixes("10.42.0.0/16")...),
ratelimit.WithMaxBuckets(100_000),
))with the endpoints that matter declaring their own:
func (r *LoginRoute) RateLimit() ratelimit.LimitConfig {
return ratelimit.LimitConfig{Rate: 5, Window: time.Minute}
}The X-RateLimit-* and Retry-After headers are useless to a browser client
unless exposed. rextension-cors exposes them by default — but
WithExposedHeaders replaces the defaults, so if you set it, include them:
cors.WithExposedHeaders(
"X-RateLimit-Limit", "X-RateLimit-Remaining", "X-RateLimit-Reset", "Retry-After",
"X-Request-Id",
)app.UsePerRoute(ratelimit.RateLimitFactory(cfg, store), rextension.PriorityRateLimit)ratelimit.RateLimitMiddleware(routerName, cfg, index, store) is the deprecated
plain-http form — it resolves the applicable limit per request, which is what
made per-endpoint limits silently inoperative for parameterized routes.
rextension-ratelimit — global, per-router and per-endpoint rate limiting for Rex · MIT · © 2026 Kryovyx
Ecosystem