1Panel + OpenResty + 腾讯 EdgeOne:全站记录真实访客 IP(Real IP)

AI摘要

正在生成中……


很多站长第一次把站点接入 CDN(尤其是腾讯 EdgeOne 这类全站加速/安全产品)后,都会遇到一个“看起来小但很致命”的问题:

源站日志里的 IP 全变成了 CDN 节点 IP
访客真实 IP 丢失了,限流、风控、Fail2ban、统计分析都不准确。

我这次在一台海外服务器上(1Panel + OpenResty)接入 EdgeOne 后,完整把“真实用户 IP 回源 + OpenResty 识别 + 全站生效 + 定时更新”这套流程跑通,并把关键坑点一起记录下来。


目标:让 $remote_addr 变回真实访客 IP

最终效果是:

  • OpenResty/Nginx 日志里 $remote_addr 记录真实用户公网 IP
  • PHP/应用层 REMOTE_ADDR 也是真实 IP
  • 而且是 全站生效(所有 1Panel 站点无需逐个重复配置)

核心原理(很重要)

要让源站拿到真实 IP,本质上是两步:

  1. EdgeOne 回源时携带真实 IP 请求头
    EdgeOne 会带类似 EO-Connecting-IP: <client_ip> 的 header(真实访客 IP)。
  2. OpenResty 使用 realip 模块“信任 EdgeOne 回源 IP 段”,并从 header 取真实 IP
    • set_real_ip_from 指定哪些“来源 IP”是可信 CDN 节点
    • real_ip_header 指定从哪个 header 取真实 IP
    • real_ip_recursive on; 适配多级代理链路

我的 1Panel 目录结构(参考)

1Panel 默认把站点配置放在:

  • /opt/1panel/www/conf.d/ —— 每个域名一个 .conf
  • /opt/1panel/www/sites/<domain>/ —— 站点目录、日志、证书

例如:

1
2
/opt/1panel/www/conf.d/vk.dps1.top.conf
/opt/1panel/www/sites/vk.dps1.top/log/access.log

最推荐:做成“全局 realip 文件”,全站继承

我最终采用的方式是:在 conf.d 下新增一个“全局配置文件”,让 OpenResty 加载后对所有站点生效。

✅ 全局文件位置

1
/opt/1panel/www/conf.d/00-edgeone-realip.conf

文件名用 00- 开头只是习惯:更容易确认加载顺序,也更直观。


自动同步 EdgeOne 回源 IP 段:脚本(稳定 + 安全)

EdgeOne 的回源 IP 段会变化(频率不高,但确实会更新)。我不想手工维护几十上百条 CIDR,所以用脚本从官方接口拉取 v4/v6 列表,生成一个 include 文件。

我踩过的坑:海外服务器 curl “时好时坏”

海外机器访问 https://api.edgeone.ai/ips?... 时,遇到过:

  • curl: (92) HTTP/2 stream ... INTERNAL_ERROR
  • 或者你用 -s 只看到“空输出”,但其实是失败了(错误被静默)

我的经验是:

  • 不要用 --http1.1 强制降级(我这台机反而会 Connection reset by peer
  • 保持默认协议(HTTP/2),但必须加 retry + 超时
  • 必须做“失败不覆盖”保护,避免同步失败时写坏配置

✅ 最终脚本:只写入 IP/CIDR + 临时文件 + 成功才覆盖

保存为:

1
/root/edgeone-ip-whitelist-sync.sh

内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#!/bin/bash
set -euo pipefail

OUT="/opt/1panel/www/conf.d/00-edgeone-realip.conf"
TMP="$(mktemp)"
BAK="${OUT}.bak"
umask 022

URL_V4="https://api.edgeone.ai/ips?version=v4"
URL_V6="https://api.edgeone.ai/ips?version=v6"

fetch() {
local url="$1"
curl -fsSL \
--retry 12 --retry-delay 1 --retry-all-errors \
--connect-timeout 8 --max-time 25 \
-H "User-Agent: edgeone-ip-sync/1.0" \
-H "Accept: text/plain" \
"$url"
}

is_cidr_token() {
local s="$1"
if echo "$s" | grep -Eq '^([0-9]{1,3}\.){3}[0-9]{1,3}(/[0-9]{1,2})?$'; then
return 0
fi
if echo "$s" | grep -Eq '^[0-9A-Fa-f:]+(/[0-9]{1,3})?$'; then
return 0
fi
return 1
}

gen_block() {
local label="$1"
local url="$2"
echo "# - ${label}"

local data
data="$(fetch "$url")"

if [ -z "${data//[[:space:]]/}" ]; then
echo "WARN: empty response for $label" >&2
return 1
fi

for i in $data; do
if is_cidr_token "$i"; then
echo "set_real_ip_from $i;"
fi
done
return 0
}

ok_any=0
{
echo "# EdgeOne real IP (auto-generated)"
echo ""

if gen_block "IPv4" "$URL_V4"; then ok_any=1; else echo "WARN: v4 fetch failed" >&2; fi
echo ""
if gen_block "IPv6" "$URL_V6"; then ok_any=1; else echo "WARN: v6 fetch failed" >&2; fi

echo ""
echo "real_ip_header EO-Connecting-IP;"
echo "real_ip_recursive on;"
} > "$TMP"

if [ "$ok_any" -eq 0 ]; then
echo "ERROR: both v4 and v6 fetch failed; not updating $OUT" >&2
rm -f "$TMP"
exit 1
fi

COUNT="$(grep -c '^set_real_ip_from ' "$TMP" || true)"
if [ "$COUNT" -lt 5 ]; then
echo "ERROR: too few IP ranges ($COUNT); not updating $OUT" >&2
rm -f "$TMP"
exit 1
fi

if [ -f "$OUT" ]; then
cp -f "$OUT" "$BAK"
fi

mv "$TMP" "$OUT"
chown root:root "$OUT"
chmod 644 "$OUT"

echo "OK: updated $OUT (set_real_ip_from lines: $COUNT)"

赋权:

1
chmod 700 /root/edgeone-ip-whitelist-sync.sh

执行一次生成配置:

1
2
/root/edgeone-ip-whitelist-sync.sh
tail -n 5 /opt/1panel/www/conf.d/00-edgeone-realip.conf

你应当能看到末尾两行:

1
2
real_ip_header EO-Connecting-IP;
real_ip_recursive on;

Reload OpenResty 让配置生效

1Panel 用的是 OpenResty,但它本质是 Nginx,reload 逻辑一样。

推荐 reload(不中断连接):

1
docker restart <openresty容器名>

如果你的环境是 nginx

1
nginx -t && nginx -s reload

总之:只要 reload 过一次,realip 就会全站生效。


验证:确认日志记录的就是访客真实 IP

最简单的验证方法:

  • 用手机 4G 或让朋友访问一次网站(确保走 CDN)
  • 然后查看站点日志:
1
tail -n 5 /www/sites/vk.dps1.top/log/access.log

如果 IP 不再是 EdgeOne 节点,而是你的公网出口 IP,那么成功。

你也可以临时把 EO-Connecting-IP 头打进日志做对照(验证完再改回去)。


定时任务:多久同步一次合适?

我建议 每天 1 次(低峰时段,比如凌晨 2 点)。

设置 crontab:

1
crontab -e

加入:

1
30 2 * * * /root/edgeone-ip-whitelist-sync.sh >>/var/log/edgeone-ip-sync.log 2>&1

查看最近同步:

1
tail -n 50 /var/log/edgeone-ip-sync.log

安全性说明:这个脚本有没有风险?

我写这套同步方案时,最关心的就是“脚本以 root 运行 + 写 Nginx 配置”,所以做了三层保护:

  1. 只写入合法的 IP/CIDR(过滤所有奇怪字符,避免注入)
  2. 先写临时文件,校验通过才原子覆盖(同步失败不影响旧配置)
  3. 配置数量太少直接拒绝覆盖(避免接口偶发异常返回空内容)

最终效果是:

同步失败不会动现有配置,最多只会在日志里报错。