fix(rate-limit): trust proxy hops explicitly for client IP resolution - #264
Merged
Merged
Conversation
Configure Express trust proxy behind Vercel/nginx/load balancers so
express-rate-limit keys clients by their real IP instead of the proxy's.
- TRUST_PROXY_HOPS env (unset/0 = no proxy trusted, <n> = trust n hops,
true = trust all) wired via app.set('trust proxy', ...)
- add server/trustProxy.test.ts proving distinct clients are limited
separately and spoofed X-Forwarded-For headers cannot bypass limits
- document TRUST_PROXY_HOPS in .env.example, .env.production (Vercel=1),
README, and CONTRIBUTING
No changes to x402 settlement semantics for paid routes.
Closes Emmy123222#177
|
Someone is attempting to deploy a commit to the Emmanuel's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@meem08 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Contributor
Author
|
@Emmy123222 review and merge |
Contributor
Author
|
Done @AbuJulaybeeb |
meem08
force-pushed
the
fix/trust-proxy-rate-limiting
branch
from
September 1, 2026 14:24
12e9670 to
d7e08eb
Compare
🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
meem08
force-pushed
the
fix/trust-proxy-rate-limiting
branch
from
September 1, 2026 20:12
d7e08eb to
f47ae2c
Compare
…main Accept upstream deletion of .env.production (production config via Vercel), merge TRUST_PROXY_HOPS row with upstream's RATE_LIMIT_PER_MINUTE, PAYMENT_AMOUNT_USDC/STROOPS, VITE_SERVER_URL, and deployment config. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
Collaborator
it didn't work |
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.
PR - Configure Express trust proxy before IP-based rate limiting
Summary
Adds an explicit, per-deployment Express
trust proxyconfiguration (TRUST_PROXY_HOPS) so thatexpress-rate-limitresolves the real client IP instead of the nearest reverse proxy's IP when the app is deployed behind Vercel, nginx, or a load balancer.Branch:
fix/trust-proxy-rate-limiting->mainStack: Express
^4.18.2+express-rate-limit^8.6.2+supertest+vitest^4.1.11Motivation / Context
Closes #177.
From the issue:
There was no
trust proxyconfiguration anywhere in the repo. The global limiter (server/index.ts) usesexpress-rate-limitwith the defaultkeyGenerator, which readsreq.ip. Consequences when deployed behind a proxy:X-Forwarded-Forimplicitly (or relied on it in logging), a single client could masquerade as many IPs and multiply its allowance.Shipping requires the hop count to be explicit per deployment (acceptance criterion) so the operator chooses the security posture rather than inheriting an ambiguous default.
Solution Overview
server/index.ts-app.set('trust proxy', ...)driven by a newTRUST_PROXY_HOPSenv var:unset/0-> trust no proxy (Express default;req.ipignoresX-Forwarded-Forentirely, so spoofing cannot add buckets).<n>-> trust exactlynhops (e.g.1for Vercel).true-> trust all proxies (opaque, fully-controlled networks only).server/trustProxy.test.ts- 6 new tests proving:X-Forwarded-Forheaders do not bypass the limit when no proxy is trusted,.env.example,.env.production(Vercel =>TRUST_PROXY_HOPS=1),README.mdenv table,CONTRIBUTING.md(trust-proxy primer + troubleshooting row).Acceptance Criteria - How This PR Satisfies
TRUST_PROXY_HOPSenv var ->app.set('trust proxy', ...)inserver/index.ts; documented in.env.example,.env.production,README.md,CONTRIBUTING.mdapp.get('trust proxy')equalsfalse,1,truefor the documented valuestrustProxy.test.ts- two clients behind one trusted proxy get separate buckets (198.51.100.11exhausted -> 429,.22-> 200) and a legitimate under-limit client passestrustProxy.test.ts- with no trusted proxy, requests flippingX-Forwarded-Forstill collapse to the single real socket and hit the limit on the 3rd requestserver/index.tscoverage); docs updated in the same PRHow to Test Locally
Test matrix (what each case proves)
TRUST_PROXY_HOPSreq.ipignoresX-Forwarded-For(spoof-safe by default)TRUST_PROXY_HOPS=11TRUST_PROXY_HOPS=truetrue10)X-Forwarded-Forvalues still capped at 2/min -> 3rd is 4291, limit 5Changes by File
server/index.tsresolveTrustProxy()+app.set('trust proxy', ...)fromTRUST_PROXY_HOPS. No change to limiter config, x402 middleware,validateQuery, or any route handler.server/trustProxy.test.ts@vitest-environment node; server tests need no DOM)..env.exampleTRUST_PROXY_HOPSwith values..env.productionTRUST_PROXY_HOPS=1(Vercel = 1 proxy hop).README.mdTRUST_PROXY_HOPSrow to the Environment Variables table.CONTRIBUTING.mdCross-Runtime Alignment - Verified
The delivery note requires Express / Vercel / browser / MCP alignment where this concern crosses runtime boundaries. The rate limiter lives only in the Express server (
server/index.ts); the Vercel serverless handlers (api/) do not useexpress-rate-limitand are unchanged. The browser and MCP consumers are unchanged.x402 settlement semantics for paid routes are preserved - no edits to
paymentMiddlewareFromConfig,ExactStellarScheme, thex402Acceptsconfig (scheme:exact price:0.001 amount:10000 network payTo), the payment replay-protection middleware, orsrc/lib/paymentIntegrity.ts. The already-verifiedserver/payment.test.tssuite is untouched.Breaking Changes / Risks
TRUST_PROXY_HOPSunset behaves exactly as before (Express default, no proxy trusted), so this is safe for direct / single-process deployments.TRUST_PROXY_HOPS=1on Vercel).server/index.tsdocuments this at the config site and.env.productionsets it for Vercel.package.json/package-lock.jsonare untouched.Checklist
npm run typecheckpassesnpm run lintpasses (0 errors)npx vitest run server/trustProxy.test.tspasses (6/6)TRUST_PROXY_HOPS).env.example,.env.production,README.md,CONTRIBUTING.md) updated in same PRCloses #177