{"_id":"@bybrave/request-ip2","name":"@bybrave/request-ip2","dist-tags":{"latest":"4.0.0"},"versions":{"4.0.0":{"name":"@bybrave/request-ip2","version":"4.0.0","description":"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","main":"lib/index.js","types":"index.d.ts","exports":{".":{"types":"./index.d.ts","import":"./index.mjs","require":"./lib/index.js"},"./package.json":"./package.json"},"scripts":{"test":"node --test test/*.test.js","test-types":"tsc --noEmit --strict test/type-declarations.ts"},"repository":{"type":"git","url":"git+https://github.com/bybraveHQ/request-ip2.git"},"bugs":{"url":"https://github.com/bybraveHQ/request-ip2/issues"},"homepage":"https://github.com/bybraveHQ/request-ip2#readme","keywords":["request","ip","ipv4","ipv6","client-ip","x-forwarded-for","cloudflare","proxy","express","fastify","middleware","security","typescript","esm"],"author":{"name":"bybrave","url":"https://github.com/bybraveHQ"},"contributors":[{"name":"Petar Bojinov","url":"author of the original request-ip"}],"license":"MIT","funding":"https://ko-fi.com/bybrave","engines":{"node":">=18"},"devDependencies":{"@types/node":"^20.14.0","typescript":"^5.5.0"},"_id":"@bybrave/request-ip2@4.0.0","gitHead":"37ec70aff462d47878dff88958c81002f73f44d7","_nodeVersion":"20.19.6","_npmVersion":"10.8.2","dist":{"integrity":"sha512-1xI3A4VAie1jmvaqsBYzgrmAXbDOFC49gstseKi5EQQlMDS4Okqakp7rbmIVd3wn5dGS1IKo/W2j17CTm5I5jQ==","shasum":"4f2fccec8a3236a5fffd76033f39ea63c74f4d95","tarball":"https://registry.npmjs.org/@bybrave/request-ip2/-/request-ip2-4.0.0.tgz","fileCount":8,"unpackedSize":24101,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDosH7CIXhvalcy+MQe2YWh7wUQJy/g8hvDxz7vrPrrpwIhAJijNcA0B/7k8+DLj/yqLe1bdnHBnGj/pd9Rbu6I/29D"}]},"_npmUser":{"name":"bybrave","email":"opmybrave@gmail.com"},"directories":{},"maintainers":[{"name":"bybrave","email":"opmybrave@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/request-ip2_4.0.0_1783464988935_0.7384183710631846"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-07T22:56:28.740Z","4.0.0":"2026-07-07T22:56:29.064Z","modified":"2026-07-07T22:56:29.564Z"},"maintainers":[{"name":"bybrave","email":"opmybrave@gmail.com"}],"description":"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","homepage":"https://github.com/bybraveHQ/request-ip2#readme","keywords":["request","ip","ipv4","ipv6","client-ip","x-forwarded-for","cloudflare","proxy","express","fastify","middleware","security","typescript","esm"],"repository":{"type":"git","url":"git+https://github.com/bybraveHQ/request-ip2.git"},"contributors":[{"name":"Petar Bojinov","url":"author of the original request-ip"}],"author":{"name":"bybrave","url":"https://github.com/bybraveHQ"},"bugs":{"url":"https://github.com/bybraveHQ/request-ip2/issues"},"license":"MIT","readme":"# @bybrave/request-ip2\n\n[![CI](https://github.com/bybraveHQ/request-ip2/actions/workflows/ci.yml/badge.svg)](https://github.com/bybraveHQ/request-ip2/actions)\n[![npm](https://img.shields.io/npm/v/%40bybrave%2Frequest-ip2)](https://www.npmjs.com/package/@bybrave/request-ip2)\n\nMaintained fork of [`request-ip`](https://github.com/pbojinov/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**.\n\nThe 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](https://github.com/pbojinov/request-ip/issues/25) — 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.\n\n```bash\nnpm install @bybrave/request-ip2\n```\n\n```js\n// CommonJS — drop-in\nconst { getClientIp, mw } = require('@bybrave/request-ip2');\n\n// ESM — named imports\nimport { getClientIp, mw } from '@bybrave/request-ip2';\n\n// TypeScript types built in — no @types/request-ip needed\n```\n\n## Secure by option, compatible by default\n\n`getClientIp(req)` with no options behaves **exactly** like `request-ip@3.3.0` (verified byte-for-byte with golden tests). The security fixes are opt-in via a second argument, so upgrading never changes behaviour silently:\n\n```js\n// Attacker sends: X-Forwarded-For: 1.2.3.4, <real-ip>\n// Your proxy appends the real IP on the right.\n\ngetClientIp(req);                    // '1.2.3.4'    — spoofable, original behaviour\ngetClientIp(req, { proxyCount: 1 }); // real client  — trust only your own proxy\n```\n\n`proxyCount` 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.\n\n```js\n// Behind Cloudflare — trust CF-Connecting-IP over the (proxy-rewritten) XFF:\ngetClientIp(req, { headers: ['cf-connecting-ip', 'x-forwarded-for'] });\n\n// Drop private / loopback / link-local addresses while resolving:\ngetClientIp(req, { filterPrivate: true });\n\n// Combine them:\ngetClientIp(req, { proxyCount: 2, filterPrivate: true });\n```\n\n## Fixes over `request-ip@3.3.0`\n\n| Fixed | Original issue |\n|---|---|\n| `X-Forwarded-For` trusts the spoofable left-most IP → opt-in `proxyCount` for secure resolution | [#25](https://github.com/pbojinov/request-ip/issues/25) (16 👍) |\n| Fixed header priority can't prefer `CF-Connecting-IP` behind Cloudflare → opt-in `headers` order | [#48](https://github.com/pbojinov/request-ip/issues/48) (7 👍), [#75](https://github.com/pbojinov/request-ip/issues/75) |\n| No way to restrict which headers are trusted → `headers` acts as a whitelist | [#26](https://github.com/pbojinov/request-ip/issues/26) |\n| Private / loopback IPs in `X-Forwarded-For` returned as the client → opt-in `filterPrivate` | [#24](https://github.com/pbojinov/request-ip/issues/24) |\n| `req.connection` triggers Fastify `FSTDEP005` deprecation warning → prefer `req.socket` | [#86](https://github.com/pbojinov/request-ip/issues/86) |\n| Broken published `dist` (missing `./is`) → clean, verified package, 0 deps | [#65](https://github.com/pbojinov/request-ip/issues/65) |\n| No built-in types (separate `@types/request-ip`, ~2.5M/month) → bundled `index.d.ts`, incl. `getClientIpFromXForwardedFor` | [#54](https://github.com/pbojinov/request-ip/issues/54) |\n| CommonJS only, no `import` support → dual ESM + CJS | — |\n\nThe \"first vs last IP in `X-Forwarded-For`\" debate ([#57](https://github.com/pbojinov/request-ip/issues/57), [#60](https://github.com/pbojinov/request-ip/issues/60)) is **by design** resolved through `proxyCount`, not by flipping the default — the correct answer depends on how many trusted proxies you run.\n\n## Migrating from request-ip\n\n```diff\n- const requestIp = require('request-ip');\n+ const requestIp = require('@bybrave/request-ip2');\n```\n\nThe 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):\n\n```diff\n- const ip = requestIp.getClientIp(req);\n+ const ip = requestIp.getClientIp(req, { proxyCount: 1 });\n```\n\nIf you use the middleware, the same options are accepted:\n\n```js\napp.use(requestIp.mw({ proxyCount: 1 }));\n// req.clientIp is now resolved securely\n```\n\n## API\n\n### `getClientIp(req, options?)`\n\nReturns 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).\n\n**Options** (all opt-in):\n\n| Option | Type | Effect |\n|---|---|---|\n| `headers` | `string[]` | Custom proxy-header priority list, replacing the default order. Also acts as a whitelist. |\n| `proxyCount` | `number` | Number of trusted reverse proxies. Selects the client IP as the Nth entry from the right of `X-Forwarded-For`. |\n| `filterPrivate` | `boolean` | Skip private / loopback / link-local addresses when resolving. |\n\n### `getClientIpFromXForwardedFor(value, options?)`\n\nParses a raw `X-Forwarded-For` header value into a single IP. Accepts the same `proxyCount` and `filterPrivate` options.\n\n### `mw(options?)`\n\nExpress/Connect middleware that attaches the resolved IP to the request. Accepts every `getClientIp` option plus `attributeName` (default `clientIp`).\n\n## Support\n\nIf this package saves you time, you can support maintenance:\n\n[![Ko-fi](https://img.shields.io/badge/Ko--fi-buy%20me%20a%20coffee-FF5E5B?logo=kofi&logoColor=white)](https://ko-fi.com/bybrave)\n[![Bitcoin](https://img.shields.io/badge/Bitcoin-BTC-F7931A?logo=bitcoin&logoColor=white)](#support)\n\nBitcoin (BTC): `bc1q37557q5jpeaxqydzwvf3jgj7zhnfpn2td3q40q`\n\n## Credits & license\n\nMIT. Based on [request-ip](https://github.com/pbojinov/request-ip) by Petar Bojinov.\n","readmeFilename":"README.md","_rev":"1-5eb2fb6939e0279b3e93fbe11cec1d09"}