---
id: realip-behind-cdn
title: 日志里全是机房 IP：realip 到底怎么还原
date: 2026-10-28
updated: 2026-10-28
category: 网络
tags: nginx, realip, CDN
minutes: 8
---

挂上 CDN 的第二天我发现两件事：访问日志里全是同一批机房 IP，nginx 的限速也变成了随机伤人——明明是别人打你，偶尔被封的却是正常用户。

原因就一个：`$remote_addr` 已经不是访客的地址了。

## 问题长什么样

```
162.158.88.31 - - [28/Oct/2026:10:12:03 +0800] "GET / HTTP/2.0" 200
162.158.88.31 - - [28/Oct/2026:10:12:04 +0800] "GET /assets/css/style.css" 200
172.71.150.6  - - [28/Oct/2026:10:12:05 +0800] "GET /post.html" 200
```

这些都是 CDN 的回源节点。日志看不出谁在访问，`limit_req` 也会把它们当成同一个来源。

## 修法：让 nginx 信任 CDN 网段

```
# /etc/nginx/snippets/cf-realip.conf
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
# … 其余网段用脚本生成，别手抄：CDN 会新增网段
real_ip_header    CF-Connecting-IP;
real_ip_recursive on;
```

然后在 server 块里 `include` 它。

三个关键点：

- **`set_real_ip_from` 是白名单**。只有这些网段发来的 `CF-Connecting-IP` 才会被采信，所以别人伪造这个头没有用。
- **`real_ip_recursive on`** 配合单一 header 才好用；如果 CDN 传的是 `X-Forwarded-For` 链，它决定了从右往左信任到哪一跳。
- **段列表要能自动更新**。我写了个脚本拉官方列表生成 snippet，失败时用内置列表兜底，这样不会因为网络问题把 nginx 配坏。

## 最容易踩的三个坑

**一、zone 定义了但没生效。** `limit_req_zone` 必须写在 http 级。放 server 里 `nginx -t` 直接报错，或者你以为是全局的其实只对某个站点生效。

**二、realip 没 include 进这个 server。** 症状很隐蔽：日志还是机房 IP，但限速"看起来在工作"。实际是所有访客共用一个桶。

**三、所有访客一起被 429。** 这是 realip 失败最危险的表现。20 个人同时用你的站，看起来像被打了。判断方法：临时在 server 里加 `add_header X-Debug-IP $remote_addr always;`，看返回的头是不是自己的出口 IP。

## 验证

```
# 用手机 4G 打开站点，然后本机看日志
tail -f /var/log/nginx/blog.access.log
# 应该出现你手机的出口 IP，不再是 162.158.x / 172.71.x
```

反过来再验一次"直连会不会被误判"：从非 CDN 网段直接请求源站 IP，看 `$remote_addr` 是不是对方真实地址、有没有被 `set_real_ip_from` 之外的值改写。

:::注意
realip 生效之后，你的限速、封禁、日志统计才第一次变准。所以它是所有"按 IP 做策略"的前提，不是可选项。
:::
