工具大全
开发者工具2026年8月2日11 次阅读约 25 分钟阅读

nginx 加了真实 IP,我的 VLESS 全挂了:一次 PROXY protocol 不对称引发的排查实录

nginx 加了真实 IP,我的 VLESS 全挂了:一次 PROXY protocol 不对称引发的排查实录

凌晨两点,客户端全部掉线。v2rayN 一片红,sing-box 日志里刷着 parse VLESS header failed

我什么都没改。就加了三行 nginx。

set_real_ip_from 0.0.0.0/0;real_ip_header X-Forwarded-For;,还有一个 proxy_protocol on;

本意挺好:日志里想看到访客的真实来源,而不是清一色的 127.0.0.1。结果这三行直接把节点干废了。

下面是我那晚从头查到尾的过程。

先搞清楚:真实 IP 是怎么传的

nginx 给后端传真实 IP,就两条路。

一条走 HTTP 头。 X-Forwarded-ForX-Real-IP,配合 real_ip 模块用。nginx 从头里读出真实 IP,把 $remote_addr 替换掉。

但 real_ip 是 http 模块,只管七层。VLESS 走裸 TCP 的时候(最常见的形态),转发它的是 nginx 的 stream 模块。real_ip 在这条链路上压根不存在,你配了等于白配。

另一条走 PROXY protocol。 这是给四层准备的。nginx 往 sing-box 方向先发一行协议头:

PROXY TCP4 8.8.8.8 1.2.3.4 54321 443\r\n

后面跟着的才是原始 TCP 流。sing-box 读完这行,就知道真实客户端是谁。

问题就出在这条路上。PROXY protocol 两边都得开,只开一边,必炸。

坑一:nginx 发了 PROXY 头,sing-box 没开接收

stream 配置长这样:

stream {
    server {
        listen 443;
        proxy_pass 127.0.0.1:8443;
        proxy_protocol on;   # 向 sing-box 发 PROXY 头
    }
}

nginx 这边发得挺欢,sing-box 那边没开接收。于是 sing-box 收到的第一个字节不是 VLESS 版本号,而是这么一行:

PROXY TCP4 8.8.8.8 1.2.3.4 54321 443

它拿这行当 VLESS 协议头去解析。UUID 对不上,版本也对不上,直接 parse VLESS header failed,断开。

表现是客户端握手秒失败,日志里全是解析错误。你第一时间想不到是 nginx 的锅——nginx -t 是过的,reload 也成功。它确实把流量"正常"转过去了,只是协议头被污染了。

解法是让 sing-box 开接收。旧版 sing-box 写在 inbound 上:

{
  "type": "vless",
  "listen": "127.0.0.1",
  "listen_port": 8443,
  "accept_proxy_protocol": true,
  "users": []
}

这里还埋着个版本坑:sing-box 1.13 之后把这个字段从 JSON schema 里拿掉了(源码标了 Deprecated/removed)。照抄两三年前的教程,配置校验直接报 unknown field。升级完 sing-box 节点突然全挂,多半就是这种"配置没变、版本变了"。

坑二:反过来,sing-box 开了,nginx 没发

对称的坑。sing-box 端 accept_proxy_protocol: true,在那傻等 PROXY 头。nginx 端没开 proxy_protocol on,上来就是原始 TCP 流。

sing-box 等不到头,连接挂在那。客户端看到的是超时,不是秒断——这是和坑一最好用的区分点。

一句话记住:秒断,是 nginx 多发了头;超时,是 nginx 少发了头。

坑三:real_ip 信任了全世界

HTTP 场景(VLESS+WS)下最常见的手滑:

set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;

0.0.0.0/0 的意思是"任何来源的 XFF 我都信"。等于跟全世界说:随便伪造,我照单全收。

于是日志里冒出 8.8.8.8 这样的"真实 IP",实际上是个连 TCP 都没握过手的假货。

更麻烦的是 sing-box 那边基于来源 IP 的规则:fallback 到伪装站、白名单放行、限流。这些要么被绕过,要么误伤自己人。

set_real_ip_from 只该写你前面那层代理的网段。

# 假设 nginx 前面只有一层内网反代
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 127.0.0.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

real_ip_recursive on 也值得单独说一句。默认 off 的时候,nginx 取 XFF 里最右边那个地址。多级代理下,最右边那个往往是倒数第二跳代理,不是真客户端。开了 recursive,nginx 从右往左找,取第一个不在信任列表里的地址——那才是真身。

坑四:裸 TCP 场景配 real_ip,纯属自我感动

VLESS 不走 WS、不走 gRPC,就是裸 TCP,尤其是配了 xtls-rprx-vision 流控的时候。这条链路走 stream 模块,real_ip 模块根本不在里面。

你要是这么写:

stream {
    server {
        listen 443;
        proxy_pass 127.0.0.1:8443;
        set_real_ip_from 0.0.0.0/0;   # ❌ stream 里没有这个指令,报错
    }
}

直接报错。stream 模块里没有 set_real_ip_from 这个指令。想给裸 TCP 传真实 IP,只有 PROXY protocol 一条路,别无分店。

能用的配置模板

裸 VLESS(TCP):nginx stream + PROXY protocol,两端对齐。

stream {
    server {
        listen 443;
        proxy_pass 127.0.0.1:8443;
        proxy_protocol on;
    }
}
// sing-box(以你装的版本 schema 为准)
{
  "type": "vless",
  "listen": "127.0.0.1",
  "listen_port": 8443,
  "accept_proxy_protocol": true,
  "users": [
    { "uuid": "你的-uuid", "flow": "xtls-rprx-vision" }
  ]
}

VLESS + WebSocket:走 HTTP 层,用 real_ip 模块,信任范围写死。

server {
    listen 443 ssl;
    server_name your.domain.com;

    set_real_ip_from 127.0.0.1;      # 只信任你自己的反代层
    set_real_ip_from 10.0.0.0/8;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

    location /vless {
        proxy_pass http://127.0.0.1:8443;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

怎么验证修好了

  1. 看 sing-box 日志journalctl -u sing-box -f。连接正常后能看到 inbound/vless-in: connection from 8.8.8.8:54321 这样的真实来源,不再是 127.0.0.1。
  2. 抓包确认 PROXY 头tcpdump -i lo -A port 8443 | head,开头应该有 PROXY TCP4 那一行。
  3. 试试能不能伪造 XFFcurl -I -H "X-Forwarded-For: 8.8.8.8" https://your.domain/vless,然后翻 nginx access log。remote_addr 真变成 8.8.8.8,说明信任范围开太大了。

最后

给 sing-box 套 nginx 加真实 IP,就三件事:

  1. 先确认流量走 http 还是 stream。这决定你用 real_ip 还是 PROXY protocol,两条路不互通。
  2. PROXY protocol 必须两端对齐。nginx 开了 sing-box 就得开,只开一边,要么秒断要么超时。
  3. set_real_ip_from 只写可信网段。写 0.0.0.0/0,等于把 IP 审计的信任权交给每个能发 HTTP 请求的人。

肯定有人要问:VLESS 本来就是加密的,日志里 IP 是真是假有什么关系?有关系。fallback 伪装、按 IP 的限流风控、被打时的溯源,全靠这一行 IP。IP 是假的,这些防线全是纸糊的。

踩完这坑我给自己立了条规矩:凡是动到代理协议传递的配置,先抓包验证,再 reload 上线。 三行配置毁一个节点的教训,一次就够了。


参考来源

✨ 本文由 DeepSeek 生成初稿,Claude 审核润色。

相关文章

GitHub 官方下场做 Stacked PR:长分支地狱有救了,但别急着全团队推广

GitHub 官方发布 gh-stack,把堆叠式 PR 从第三方插件变成官方工作流。本文拆解堆叠 PR 机制、为什么官方下场是大事、十分钟上手命令,以及四个不该用的场景。

developer2026年8月2日13 min
10

码农早餐 · 2026-08-02

今日精选 8 条 AI 圈情报:微软发布 TRELLIS.2,用结构化潜空间做 3D 生成;美国供水系统遭黑客攻击,证据指向伊朗;GitHub 推出官方 Stacked PR 工具;等...

daily-intel2026年8月2日11 min
37

码农早餐 · 2026-08-01

今日精选 8 条 AI 圈情报:DeepSeek V4 Flash 低调上线,官方文档与第三方评测同步更新;Kimi K3 模型新进展:本地跑得动,但背后是阿里万卡集群;Hugging Face 遭入侵,Tailscale 未能阻止;等...

daily-intel2026年8月1日13 min
80

Opus 5 发布一周:数据说它更准,体感说它更烦——这其实是同一件事

Claude Opus 5 发布一周,社区结论罕见地两极:CodeRabbit 用 96 个真实 bug 实测出「精确率史上最高、但漏掉的真 bug 更多、nitpick 翻 4 倍」;产品圈 KOL Claire Vo 一边发明「neurotic AF」和「Claudeslop」骂它,一边在自己的 7 模型盲测里把它排到第一。本文把两条线拧在一起读:精确率升、召回率降、话痨、胆小、拒碰别人的分支——五个现象背后是同一个旋钮。附一份基于失败模式选择的实用指南:什么时候用 x-high,什么时候降档,以及为什么该删掉你为上一代模型调的提示词。

claudeanthropic+6
ai-tutorials2026年7月31日6 min
139