@bybrave/request-ip2
v4.0.0
Published
Maintained fork of request-ip — get the client IP from a request, now with secure X-Forwarded-For parsing, dual ESM+CJS, built-in TypeScript types and 0 dependencies
Downloads
27
Maintainers
Readme
@bybrave/request-ip2
Maintained fork of request-ip — retrieve the client IP address from an incoming HTTP request — with secure X-Forwarded-For parsing, dual ESM + CommonJS, built-in TypeScript types and 0 dependencies.
The original package has ~10.9M downloads/month and no release since 2022. Its default X-Forwarded-For handling trusts the left-most (client-controlled) IP, which is spoofable — the single most-upvoted issue on the repo. This fork keeps the original behaviour byte-for-byte by default and adds opt-in options to resolve the IP safely.
npm install @bybrave/request-ip2// CommonJS — drop-in
const { getClientIp, mw } = require('@bybrave/request-ip2');
// ESM — named imports
import { getClientIp, mw } from '@bybrave/request-ip2';
// TypeScript types built in — no @types/request-ip neededSecure by option, compatible by default
getClientIp(req) with no options behaves exactly like [email protected] (verified byte-for-byte with golden tests). The security fixes are opt-in via a second argument, so upgrading never changes behaviour silently:
// Attacker sends: X-Forwarded-For: 1.2.3.4, <real-ip>
// Your proxy appends the real IP on the right.
getClientIp(req); // '1.2.3.4' — spoofable, original behaviour
getClientIp(req, { proxyCount: 1 }); // real client — trust only your own proxyproxyCount is the number of trusted reverse proxies in front of your app. The client IP is taken as the Nth entry from the right of the chain — the part a client cannot forge — instead of the spoofable left-most value.
// Behind Cloudflare — trust CF-Connecting-IP over the (proxy-rewritten) XFF:
getClientIp(req, { headers: ['cf-connecting-ip', 'x-forwarded-for'] });
// Drop private / loopback / link-local addresses while resolving:
getClientIp(req, { filterPrivate: true });
// Combine them:
getClientIp(req, { proxyCount: 2, filterPrivate: true });Fixes over [email protected]
| Fixed | Original issue |
|---|---|
| X-Forwarded-For trusts the spoofable left-most IP → opt-in proxyCount for secure resolution | #25 (16 👍) |
| Fixed header priority can't prefer CF-Connecting-IP behind Cloudflare → opt-in headers order | #48 (7 👍), #75 |
| No way to restrict which headers are trusted → headers acts as a whitelist | #26 |
| Private / loopback IPs in X-Forwarded-For returned as the client → opt-in filterPrivate | #24 |
| req.connection triggers Fastify FSTDEP005 deprecation warning → prefer req.socket | #86 |
| Broken published dist (missing ./is) → clean, verified package, 0 deps | #65 |
| No built-in types (separate @types/request-ip, ~2.5M/month) → bundled index.d.ts, incl. getClientIpFromXForwardedFor | #54 |
| CommonJS only, no import support → dual ESM + CJS | — |
The "first vs last IP in X-Forwarded-For" debate (#57, #60) is by design resolved through proxyCount, not by flipping the default — the correct answer depends on how many trusted proxies you run.
Migrating from request-ip
- const requestIp = require('request-ip');
+ const requestIp = require('@bybrave/request-ip2');The API is identical. Nothing changes until you pass options. When you're ready to harden IP resolution, set proxyCount to the number of proxies in front of your app (e.g. 1 behind a single load balancer):
- const ip = requestIp.getClientIp(req);
+ const ip = requestIp.getClientIp(req, { proxyCount: 1 });If you use the middleware, the same options are accepted:
app.use(requestIp.mw({ proxyCount: 1 }));
// req.clientIp is now resolved securelyAPI
getClientIp(req, options?)
Returns the client IP as a string, or null if none could be determined. Reads proxy headers first (in priority order), then req.socket / req.connection, req.info (hapi), AWS Lambda requestContext.identity.sourceIp, and recurses into req.raw (Fastify).
Options (all opt-in):
| Option | Type | Effect |
|---|---|---|
| headers | string[] | Custom proxy-header priority list, replacing the default order. Also acts as a whitelist. |
| proxyCount | number | Number of trusted reverse proxies. Selects the client IP as the Nth entry from the right of X-Forwarded-For. |
| filterPrivate | boolean | Skip private / loopback / link-local addresses when resolving. |
getClientIpFromXForwardedFor(value, options?)
Parses a raw X-Forwarded-For header value into a single IP. Accepts the same proxyCount and filterPrivate options.
mw(options?)
Express/Connect middleware that attaches the resolved IP to the request. Accepts every getClientIp option plus attributeName (default clientIp).
Support
If this package saves you time, you can support maintenance:
Bitcoin (BTC): bc1q37557q5jpeaxqydzwvf3jgj7zhnfpn2td3q40q
Credits & license
MIT. Based on request-ip by Petar Bojinov.
