PostHog / PostHog/posthog

Graduate `/flags` rate limiting from log-only to warn-then-enforce

Open
#49,570 1 comment 0 reactions 1 assignee View on GitHub

@haacked is already working on this.

Since Mar 9, 2026.

enhancement
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 KeyedRateLimiter in flags_rate_limiter.rs currently holds a single governor limiter and a log_only bool.
    Replace with two governor limiters (warn + enforce) or a single limiter checked against two token levels.
  • Reuse the parse_rate_string helper from rate_parser.rs for per-team config parsing.
  • Add the X-PostHog-Rate-Limit-Warning header in endpoint.rs before returning the response,
    so SDKs can detect it without parsing the body.
  • Existing tests in tests/test_rate_limiting.rs cover the current modes;
    extend them with warn-then-enforce scenarios and per-team override cases.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.