Graduate `/flags` rate limiting from log-only to warn-then-enforce
@haacked is already working on this.
Since Mar 9, 2026.
- Dominant language
- Python
- Stars
- 39.9k
- Forks
- 3.4k
- Avg merge
- 6h 51m
- Merged PRs (30d)
- 232
Description
Problem
The token-based and IP-based rate limiters on /flags are fully implemented but currently operate in log-only mode by default (FLAGS_RATE_LIMIT_LOG_ONLY=true, FLAGS_IP_RATE_LIMIT_LOG_ONLY=true).
When a client exceeds the limit, the service logs a warning and increments a counter, but the request goes through. There is no middle ground between "silently allow everything" and "hard 429".
Additionally, the /flags endpoint has no per-team rate configuration. The flag-definitions endpoint already supports per-team custom rates via FLAG_DEFINITIONS_RATE_LIMITS_JSON, but /flags applies the same global threshold to every token. Teams with legitimately high traffic (large SDKs, high-volume products) get the same limit as everyone else.
Proposal
1. Two-tier thresholds: warn, then enforce
Replace the current binary log_only flag with two configurable thresholds per limiter:
- Warn threshold (lower): the request proceeds, but the response includes a warning header (e.g.
X-PostHog-Rate-Limit-Warning: true) and the service emits a metric + log. SDK authors and customers can use this signal to back off before hitting a hard limit. - Enforce threshold (higher): the request is rejected with 429, same as the current enforced behavior.
Example configuration:
FLAGS_BUCKET_WARN_CAPACITY=300
FLAGS_BUCKET_ENFORCE_CAPACITY=500
FLAGS_BUCKET_REPLENISH_RATE=10.0
A request that exhausts tokens below the warn threshold gets the warning header.
A request that exhausts tokens below the enforce threshold gets a 429.
The same two-tier model applies to the IP-based limiter.
2. Per-team rate limits on /flags
Extend FlagsRateLimiter to support per-team overrides, mirroring how FlagDefinitionsRateLimiter already handles custom rates.
The key used for rate limiting is the project API token. Per-team overrides would map a token (or team ID, resolved after auth) to custom warn/enforce thresholds.
Configuration:
FLAGS_RATE_LIMITS_JSON='{"team_123": "20/s", "team_456": "50/s"}'
Teams not listed fall back to the global defaults.
Implementation notes
- The
KeyedRateLimiterinflags_rate_limiter.rscurrently holds a singlegovernorlimiter and alog_onlybool.
Replace with twogovernorlimiters (warn + enforce) or a single limiter checked against two token levels. - Reuse the
parse_rate_stringhelper fromrate_parser.rsfor per-team config parsing. - Add the
X-PostHog-Rate-Limit-Warningheader inendpoint.rsbefore returning the response,
so SDKs can detect it without parsing the body. - Existing tests in
tests/test_rate_limiting.rscover the current modes;
extend them with warn-then-enforce scenarios and per-team override cases.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.