fix(security): prevent rate limit bypass via X-Forwarded-For spoofing#338
Merged
Priyanshu-byte-coder merged 1 commit intoMay 19, 2026
Conversation
The public profile endpoint used x-forwarded-for as the primary key for IP-based rate limiting. This header is fully client-controlled: an attacker can set it to a different value on every request, making the 30 req/min limit completely ineffective. Switch to req.ip as the primary key. The Next.js/Vercel runtime derives req.ip from the verified network-layer source address and it cannot be set by the caller. Fall back to x-real-ip only when req.ip is unavailable. This prevents unauthenticated callers from exhausting the shared GITHUB_TOKEN quota (5 000 req/hr) and degrading service for all users.
|
@anshul23102 is attempting to deploy a commit to the PRIYANSHU DOSHI's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Thanks for your first PR on DevTrack! 🎉
A maintainer will review it within 48 hours. While you wait:
- Make sure CI is passing (type-check + lint)
- Double-check the PR description is filled out and the issue is linked
- Feel free to ask questions in Discussions if you need help
Priyanshu-byte-coder
approved these changes
May 19, 2026
Owner
Priyanshu-byte-coder
left a comment
There was a problem hiding this comment.
Correct. x-forwarded-for is a plain request header — any caller can set it arbitrarily per request, completely bypassing the rate limit. req.ip is populated by the Vercel/Next.js runtime from the verified network-layer source address and cannot be spoofed. Clean one-liner fix.
4a073d0
into
Priyanshu-byte-coder:main
7 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #337
What was wrong
getRateLimitKey()insrc/app/api/public/[username]/route.tsusedx-forwarded-foras the primary source for the rate limit key. This header is a plain HTTP request header — any caller can set it to an arbitrary value on every request. This made the 30 req/min in-memory rate limiter completely ineffective against anyone who rotated the header, allowing unbounded requests to the public profile endpoint and full exhaustion of the sharedGITHUB_TOKENquota (5,000 req/hr).What changed
x-forwarded-forfrom the rate limit key lookup chainreq.ipis now the primary key — the Next.js/Vercel runtime populates this from the verified network-layer source address, which cannot be set by the callerx-real-ipis kept as a fallback for non-Vercel self-hosted deploymentsx-forwarded-foris clear to future contributorsFiles changed
src/app/api/public/[username]/route.ts—getRateLimitKey()function onlyTesting
The fix can be verified locally by running the endpoint with a custom
X-Forwarded-Forheader after more than 30 requests — the 429 response should now trigger based on the actual connection IP regardless of the header value.Type of change