Skip to content

fix(rate-limit): add Retry-After header to 429 responses and fix TOCTOU race in Redis rate limiter#6156

Closed
Aamod-Dev wants to merge 1 commit into
JhaSourav07:mainfrom
Aamod-Dev:fix/rate-limit-retry-after-5857
Closed

fix(rate-limit): add Retry-After header to 429 responses and fix TOCTOU race in Redis rate limiter#6156
Aamod-Dev wants to merge 1 commit into
JhaSourav07:mainfrom
Aamod-Dev:fix/rate-limit-retry-after-5857

Conversation

@Aamod-Dev

Copy link
Copy Markdown
Collaborator

Summary

Fixes the rate-limiting issues documented in #6149. Addresses the missing \Retry-After\ header (as originally requested in #5857) and fixes two bugs in the Redis rate-limit path.

Changes

1. Added \Retry-After\ header to all 429 responses

Files: \lib/rate-limit.ts, \middleware.ts\

The \getRateLimitHeaders()\ helper now includes the standard \Retry-After\ HTTP header in addition to the existing \X-RateLimit-*\ headers. The middleware now uses this helper instead of manually constructing headers.

Before: 429 responses had \X-RateLimit-Limit, \X-RateLimit-Remaining, \X-RateLimit-Reset\ but no \Retry-After.

After: 429 responses include:
\
Retry-After: 42
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1712345678000
\\

2. Fixed TOCTOU race condition in Redis rate-limit path

File: \lib/rate-limit.ts:72-121\

The \RateLimiter.checkWithResult()\ method previously used a non-atomic read-then-write pattern:

  1. \GET\ current count
  2. \TTL\ check
  3. If under limit, \INCR\ + \EXPIRE\

Under concurrent load, multiple requests could all read the same sub-limit count and all pass through. Replaced with a single atomic \INCR\ + \EXPIRE\ pipeline that matches the approach used in the
ateLimit()\ helper function.

3. Fixed \

emaining()\ method for Redis-backed deployments

File: \lib/rate-limit.ts:195-198\

The
emaining()\ method was reading from local cache key
atelimit:\ while the Redis path stored under
atelimit_class:\. When Redis was active, this always returned the full limit. Now queries Redis directly when available using the correct key prefix.

Related Issues

Closes: #6149
Related: #5857

…ng() key prefix

- Add Retry-After header to getRateLimitHeaders() for RFC-compliant 429 responses
- Fix middleware.ts to use getRateLimitHeaders() instead of manual headers
- Fix TOCTOU race in RateLimiter.checkWithResult() Redis path
  (replaced GET+TTL+INCR with single atomic INCR+EXPIRE pipeline)
- Fix remaining() to use correct key prefix (ratelimit:) and query Redis when available

Related to JhaSourav07#5857
@Aamod-Dev Aamod-Dev added bug Something isn't working security labels Jun 21, 2026
@vercel

vercel Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

@Aamod-Dev is attempting to deploy a commit to the jhasourav07's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

Copy link
Copy Markdown
Contributor

👋 Hey @Aamod-Dev! Thanks for your interest in contributing to CommitPulse! 🙏

Unfortunately, this PR has been automatically closed because you are not assigned to the linked issue #6149 — fix(rate-limit): add Retry-After header to 429 responses and fix TOCTOU race in Redis rate limiter.

To avoid this in the future, please follow these steps:

  1. Claim the issue — Comment /claim on #6149 if you are the issue author, or ask a maintainer to /assign you.
  2. Wait for confirmation — The bot will confirm your assignment with a ✅ reply.
  3. Then open your PR — Link the issue with Fixes #6149 in your description.

💡 You can be assigned to up to 5 open issues at a time. Check your current assignments before claiming a new one.

We look forward to your contribution once you're assigned! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working security

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(rate-limit): add Retry-After header to 429 responses and fix TOCTOU race in Redis rate limiter

1 participant