# Diagnosing a misfiring @upstash/ratelimit

## Symptom A: legitimate users get 429s

- Check the algorithm. Fixed window counts hard resets at window
  boundaries: two requests straddling the boundary both count fully.
  Sliding window smooths this.
- Check the identifier. A constant identifier like "api" applies one
  global limit to all users. One abusive client burns everyones
  quota. Use per-user or per-IP identifiers.
- Check dynamic limits. If enabled, a stale dynamic limit override
  can sit far below your intended rate. Read it back with
getDynamicLimit.

## Symptom B: abuse gets through

- Fixed window leaks bursts at boundaries by design. If bursts are
your threat, switch to sliding window or token bucket.
- The runtime cache only stores identifiers AFTER they were rate
  limited once. On serverless cold starts the cache is empty, so the
  first burst of every cold instance always hits Redis. That is a
  latency cost, not a bypass: Redis still enforces.
- Multi-region without waitUntil on pending: regions diverge and each
  enforces only its local view.

## Step 3: confirm with getRemaining

getRemaining(identifier) shows the current quota state without
consuming. Compare it against what you configured. If the numbers do
not match your config, the limiter instance in production differs
from the one you think you deployed.

## Fix and verify

Align algorithm, identifier granularity, and dynamic limits with the
threat. Then burst-test: send 2x the limit in a tight loop and
confirm roughly the expected fraction gets 429s.