亮点✨
- Spoof-resistant - - - - - -走
X-Forwarded-For从右边,停在第一个不受信任的跃点,如Expresstrust proxy - 无需框架即可工作 -raw
node:http,高雅ctx.req, Next.js页面API, Express v4/v5, fasttify, NestJS - 选择加入供应商标题 - - - - - -
cf-connecting-ip,true-client-ip,和类似的是只读的,当你列出它们 - RFC 7239
Forwarded-引用字符串,IPv6,端口,unknown,以及混淆的标识符 - CIDR信任列表 —比较解析后的IPv4地址和IPv6地址
- 归一化输出 - - - - - -
::ffff:1.2.3.4变成了1.2.3.4;IPv6是小写和压缩的
安装📦
需要Node.js >= 22.12。
npm install get-client-ip
# or
pnpm add get-client-ip
快速启动🚀
import express from "express";
import { getClientIp } from "get-client-ip";
const app = express();
// Middleware — populates req.clientIp and req.clientIps
app.use(getClientIp);
app.get("/me", (req, res) => {
res.json({ ip: req.clientIp, ips: req.clientIps });
});
// Or standalone — call directly in a route
app.get("/ip", (req, res) => {
res.json({ ip: getClientIp(req) });
});
app.listen(3000);
无框架的:
import http from "node:http";
import { getClientIp } from "get-client-ip";
http.createServer((req, res) => res.end(getClientIp(req))).listen(3000);
API参考📖
getClientIp(req, options?): string | undefined
返回客户端IP,或 undefined 当没有地址是已知的。也设置 req.clientIp 和 req.clientIps. 如果扔 req 缺少或 options 是无效的。
直接传递给 app.use(getClientIp) 将其作为带有默认选项的中间件运行。
getClientIpMiddleware(options?)
返回 (req, res, next) 使用的中间件 options. 它验证 options 一次,在创作。
import { DEFAULT_TRUSTED_PROXIES, getClientIpMiddleware } from "get-client-ip";
app.use(getClientIpMiddleware({ trustedProxies: [...DEFAULT_TRUSTED_PROXIES, "203.0.113.0/24"] }));
选项
| 选项 | 类型 | 默认的 | 描述 |
|---|---|---|---|
trustedProxies | string[] | DEFAULT_TRUSTED_PROXIES | 您信任其转发头的代理的ip或cidr。 [] 不信任任何人。 |
forwardedHeader | "x-forwarded-for" | "forwarded" | "x-forwarded-for" | 代理附加到的标头。另一个被忽略。 |
trustedHeaders | string[] | [] | 单IP标头您的边缘总是覆盖,例如. ["cf-connecting-ip"]. 按顺序检查。 |
DEFAULT_TRUSTED_PROXIES 是 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, ::1/128, fc00::/7, fe80::/10。CGNAT (100.64.0.0/10) 不受信任。
req.clientIp 和 req.clientIps
req.clientIp; // string | undefined — the client IP
req.clientIps; // [string, ...string[]] | undefined — the client, then each trusted hop toward your server
包裹增加了快递 Request 类型,因此不需要手动声明。
如何解析IP📋
trustedHeaders-仅当套接字对等体是可信代理时,从列出的标头中获得第一个有效IP。req.ip-当它是一个有效的IP外trustedProxies. Express和fasttify从它们自己的信任设置中设置它。- 如果
req.ip本身是一个受信任的代理 (例如,没有trust proxy在nginx之后,它是代理,而不是客户端。像步骤4一样遍历链。req.ip仅当未找到不受信任的跃点时才保留。
- 如果
- 套接字地址 -当套接字对等体不是受信任的代理时,将忽略标头。
- 转发链 ——读
X-Forwarded-For(或Forwarded) 从右边。跳过每个受信任的代理。返回第一个不受信任的地址。- 如果每个跳都是可信的,则返回最左边的跳。
- 一个
unknown,混淆的(_hidden),或无效跳停止行走。返回最近的可信跳。
示例: nginx on 10.0.0.5 附加它看到的peer。客户端发送 X-Forwarded-For: 6.6.6.6,所以你的应用程序接收 6.6.6.6, 203.0.113.9。结果是 203.0.113.9,不是恶搞的 6.6.6.6。
部署指南🧭
| 设置 | 配置 |
|---|---|
| nginx、HAProxy或私有IP上的负载均衡器 | 违约 |
| 代理背后的快递 | 默认工作; 设置Express trust proxy 同样适用 |
| 私有负载均衡器前面的Cloudflare | { trustedHeaders: ["cf-connecting-ip"] } |
写rfc7239的代理 Forwarded | { forwardedHeader: "forwarded" } |
| 公共IP上的代理 | { trustedProxies: [...DEFAULT_TRUSTED_PROXIES, "<proxy CIDR>"] } |
| 直接在互联网上 | { trustedProxies: [] } |
NestJS🧩
import { Controller, Get, Req } from "@nestjs/common";
import type { Request } from "express";
import { getClientIp } from "get-client-ip";
@Controller()
export class AppController {
@Get("ip")
getIp(@Req() req: Request) {
return { ip: getClientIp(req) };
}
}
安全🛡️
- 仅列出您的边缘覆盖的标头。 一个
trustedHeaders入口由客户端控制,除非每个请求都经过该边缘。让你的原点只能通过它到达。 - 选择代理写入的标头。 如果您的代理追加
X-Forwarded-For,一个客户端发送的ForwardedHeader被忽略,反之亦然。 - 受信任范围内的客户端可以欺骗。 受信任网络上的主机可以发送任何
X-Forwarded-For. 狭窄的trustedProxies到你代理的地址。 - 一个不受信任的
req.ip是可信的。 如果您的框架的代理信任比trustedProxies,req.ip可以来自客户端提供的标头。
v5中的重大更改⚠️
- 需要Node.js >= 22.12。
X-Forwarded-For从右边读。 v4返回最左边的条目,任何客户端都可以设置。现在返回右侧的第一个不受信任的跃点。- 供应商头需要选择加入。
cf-connecting-ip,true-client-ip,fastly-client-ip,x-appengine-user-ip,cf-pseudo-ipv4,x-client-ip,x-real-ip,x-cluster-client-ip,forwarded-for,x-forwarded被忽略,除非列在trustedHeaders。 - 受信任的
req.ip不再赢了。 当req.ip在里面trustedProxies(表达不trust proxy在私有或环回代理之后),转发链被走。v4返回了代理的地址。通过trustedProxies: []以保留v4结果。 Forwarded需要选择加入。 集forwardedHeader: "forwarded"。X-Forwarded-For然后被忽略。- 第二个论点是现在
options。getClientIp(req, res)不再意味着中间件; 使用getClientIp(req, res, next),app.use(getClientIp),或getClientIpMiddleware()。 req.clientIps改变的意义。 它是客户端,后面是指向服务器的可信跃点,最后是套接字对等端。v4列出了每个标题条目。- 输出归一化。 ipv4 -映射IPv6 (
::ffff:1.2.3.4) 成为IPv4,包括req.ip. IPv6是小写和压缩的,它的区域ID被删除(fe80::1%eth0变成了fe80::1). - 一个引用
X-Forwarded-For表项是无效的一跳。 中去掉了引号"203.0.113.9"。代理从不引用它,所以引用的条目是客户端编写的,并停止行走。 - 中间件总是设置
req.clientIp。 它是undefined当没有地址解析时。v4保持不变。 - IPv6 trust使用真实CIDR匹配。
fc::1不再受信任 (它不在fc00::/7).fe80::/10现在覆盖fe80::-febf::。 0.0.0.0和::是无效跳数。
致谢🙏
灵感来自 佩博吉诺夫的 处理客户端IP检测。
贡献🤝
- 打开一个 问题 或者特性请求
- 提交PR以改进包装
- 如果你觉得它有用,就用星号标记它