UNPKG

get-client-ip

Version:

📍 A Lightweight Utility for Extracting the Real Client IP Address from Incoming HTTP Requests

1 lines 6.72 kB
{"version":3,"sources":["../src/index.ts"],"names":["isIP","ip"],"mappings":"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","file":"index.cjs","sourcesContent":["import { isIP } from 'node:net';\r\nimport type { NextFunction, Request, Response } from 'express';\r\n\r\ntype NoneEmptyArray<T> = [T, ...T[]];\r\n\r\nfunction $isIP(ip: unknown): ip is string {\r\n return typeof ip === 'string' && isIP(ip) !== 0;\r\n}\r\n\r\nconst LOOKUP_HEADERS = [\r\n 'x-client-ip',\r\n 'x-forwarded-for',\r\n 'forwarded-for',\r\n 'x-forwarded',\r\n 'x-real-ip',\r\n 'cf-connecting-ip',\r\n 'true-client-ip',\r\n 'x-cluster-client-ip',\r\n 'fastly-client-ip',\r\n 'x-appengine-user-ip',\r\n 'cf-pseudo-ipv4',\r\n];\r\n\r\nfunction $extractIpFromHeaders(req: Request): NoneEmptyArray<string> | null {\r\n if ($isIP(req.ip)) return [req.ip];\r\n\r\n if (!req.headers) return null;\r\n if ($isIP(req.headers.forwarded)) return [req.headers.forwarded];\r\n\r\n for (let i = 0; i < LOOKUP_HEADERS.length; i++) {\r\n const ip = req.headers[LOOKUP_HEADERS[i] as string];\r\n if (!ip) continue;\r\n if (Array.isArray(ip)) {\r\n const filteredIps = ip.filter((item) => $isIP(item.trim()));\r\n if (filteredIps.length > 0) return filteredIps.map((item) => item.trim()) as NoneEmptyArray<string>;\r\n }\r\n\r\n if (typeof ip === 'string') {\r\n if ($isIP(ip.trim())) return [ip.trim()];\r\n if (!ip.includes(',')) continue;\r\n const filteredIps = ip.split(',').filter((ip) => $isIP(ip.trim()));\r\n if (filteredIps.length > 0) {\r\n return filteredIps.map((item) => item.trim()) as NoneEmptyArray<string>;\r\n }\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n// biome-ignore-start lint/correctness/noUnusedFunctionParameters: Needed for Express middleware signature\r\n\r\n/**\r\n * Extracts the client's IP address from an incoming Express request.\r\n *\r\n * This function works both as a standalone utility and as Express middleware.\r\n * It attempts to detect the IP by inspecting common proxy-related headers\r\n * such as `x-forwarded-for`, `x-real-ip`, and others. If no valid IP is found\r\n * in the headers, it falls back to `req.socket.remoteAddress` or `req.connection.remoteAddress`.\r\n *\r\n * When used as middleware, it populates:\r\n * - `req.clientIp`: The first valid IP address found.\r\n * - `req.clientIps`: A non-empty array of all valid IPs found.\r\n *\r\n * @param req - The Express request object.\r\n * @param res - (Optional) The Express response object. Included to support middleware signature.\r\n * @param next - (Optional) The next function in the Express middleware chain.\r\n *\r\n * @returns The first detected client IP address as a string, or `undefined` if none is found.\r\n *\r\n * @throws Will throw an error if the `req` argument is not defined.\r\n *\r\n * @example\r\n * // Standalone usage:\r\n * app.get('/standalone-ip', (req, res) => {\r\n * const ip = getClientIp(req);\r\n * res.status(200).json({ ip });\r\n * });\r\n *\r\n * @example\r\n * // Middleware usage:\r\n * app.use(getClientIp);\r\n * app.get('/middleware-ip', (req, res) => {\r\n * res.status(200).json({ ip: req.clientIp, ips: req.clientIps });\r\n * });\r\n */\r\nexport function getClientIp(req: Request, res?: Response, next?: NextFunction): string | undefined {\r\n if (!req) throw new Error('Request is undefined');\r\n\r\n const ips = $extractIpFromHeaders(req);\r\n if (ips && ips.length > 0) {\r\n req.clientIp = ips[0];\r\n req.clientIps = ips;\r\n next?.();\r\n return ips[0];\r\n }\r\n\r\n if ($isIP(req.socket.remoteAddress)) {\r\n req.clientIp = req.socket.remoteAddress;\r\n req.clientIps = [req.socket.remoteAddress];\r\n next?.();\r\n return req.socket.remoteAddress;\r\n }\r\n\r\n if ($isIP(req.connection.remoteAddress)) {\r\n req.clientIp = req.connection.remoteAddress;\r\n req.clientIps = [req.connection.remoteAddress];\r\n next?.();\r\n return req.connection.remoteAddress;\r\n }\r\n}\r\n\r\n// biome-ignore-end lint/correctness/noUnusedFunctionParameters: Needed for Express middleware signature\r\n\r\ndeclare global {\r\n namespace Express {\r\n export interface Request {\r\n /** The first IP address extracted from the request headers */\r\n clientIp?: string;\r\n /** The array of all IP addresses extracted from the request headers */\r\n clientIps?: NoneEmptyArray<string>;\r\n }\r\n }\r\n}\r\n"]}