← 返回教程
教程

cloudflare安全规则限速

Cloudflare API 限流与源站防护配置总结

1. 背景

服务器安全组已经关闭了公网 80443 入站端口,仅保留 SSH 白名单访问。

但是应用日志中仍然出现了大量请求,例如:

{
  "method": "GET",
  "path": "/api/app/getserviceinfo",
  "status": 404,
  "clientIp": "172.x.x.x"
}

这些请求看起来像自动化扫描器在探测接口路径。

经过排查,发现请求并不是直接通过服务器公网 80/443 端口进入,而是可能通过以下链路进入:

外部请求
  -> Cloudflare
  -> Cloudflare Tunnel / 反向代理
  -> Docker 网络
  -> 应用服务

因此,即使云服务器安全组关闭了 80/443,只要 Cloudflare Tunnel 或代理入口仍然存在,外部请求仍可能通过域名进入应用。


2. 关键判断

应用日志中的客户端 IP 类似:

172.x.x.x

这类地址通常是 Docker 内部网络地址,不是攻击者真实公网 IP。

说明请求大概率经过了:

Cloudflare / Traefik / Dokploy / Docker 网关

应用看到的是内部网关 IP,而不是用户真实 IP。

建议应用日志增加以下字段,方便后续定位真实来源:

Host
CF-Connecting-IP
X-Forwarded-For
X-Real-IP
User-Agent
Path
Method

Gin 示例:

log.Printf(
	"ClientIP=%s Host=%s CF=%s XFF=%s RealIP=%s UA=%s Path=%s Method=%s",
	c.ClientIP(),
	c.Request.Host,
	c.GetHeader("CF-Connecting-IP"),
	c.GetHeader("X-Forwarded-For"),
	c.GetHeader("X-Real-IP"),
	c.GetHeader("User-Agent"),
	c.Request.URL.Path,
	c.Request.Method,
)

3. 攻击者如何知道 IP 或域名

常见来源包括:

3.1 域名 DNS 暴露

只要域名或子域名曾经解析到服务器 IP,就可能被记录在被动 DNS 数据库中。

例如:

api.example.com -> 源站 IP
admin.example.com -> 源站 IP

即使后续删除或切换到 Cloudflare,也可能存在历史记录。

3.2 证书透明日志

只要给子域名申请过 HTTPS 证书,域名可能会出现在公开的证书透明日志中。

扫描器会批量监听新证书并自动探测相关域名。

3.3 云服务器 IP 段扫描

扫描器不一定知道域名,也可能直接扫描云厂商 IP 段。

常见扫描端口包括:

80
443
22
3000
8080
5432
6379

3.4 前端代码暴露 API 地址

如果前端代码中包含:

const API_BASE_URL = "https://api.example.com"

用户和扫描器都可以通过浏览器源码或抓包看到 API 域名。

API 域名本身不应依赖隐藏来保证安全。


4. Cloudflare 为什么一开始没有拦截

Cloudflare 默认不会因为“同一秒出现请求”就一定拦截。

未拦截的常见原因:

1. 未配置 Rate Limiting 规则;
2. 请求只是普通 GET/HEAD 探测,没有明显攻击特征;
3. 默认 WAF 主要拦截 SQL 注入、XSS、路径穿越等攻击载荷;
4. Bot 防护不是万能的;
5. 请求频率没有超过已配置的限流阈值。

例如配置为:

30 次 / 10 秒

但脚本是:

1 秒 1 次

那么 10 秒内只有约 10 次请求,没有超过阈值,因此不会触发限流。


5. Cloudflare API 限流配置

5.1 配置入口

进入 Cloudflare 控制台:

Websites
  -> 选择站点
  -> Security
  -> WAF
  -> Rate limiting rules
  -> Create rule

或新版入口:

Security
  -> Security rules
  -> Rate limiting rules
  -> Create rule

6. API 限流规则

规则名称

api限速

匹配条件

建议同时限制 Host 和 API 路径,避免影响其他子域名。

表达式示例:

(http.host eq "api.example.com" and http.request.uri.path wildcard r"/api/*")

含义:

只限制 api.example.com 这个 API 域名
并且只限制 /api/* 开头的接口

相同特征

IP

表示按单个客户端 IP 计数。

阈值

生产环境建议:

30 次 / 10 秒

或更严格:

20 次 / 10 秒

测试规则是否生效时,可以临时设置为:

5 次 / 10 秒

动作

测试阶段:

Managed Challenge

确认没有误伤后,可以改为:

Block

持续时间

10 分钟

7. 推荐配置

普通 API 限流

规则名称:
api限速

匹配条件:
Host = api.example.com
AND URI 路径 通配符 /api/*

表达式:
(http.host eq "api.example.com" and http.request.uri.path wildcard r"/api/*")

相同特征:
IP

阈值:
30 次 / 10 秒

动作:
Managed Challenge

持续时间:
10 分钟

8. 测试配置是否生效

Linux / macOS:

for i in {1..20}; do
  curl -I https://api.example.com/api/test
done

Windows PowerShell:

1..20 | ForEach-Object {
  curl.exe -I https://api.example.com/api/test
}

测试时建议临时设置:

5 次 / 10 秒

如果规则生效,前几次请求会正常到达应用,超过阈值后会被 Cloudflare 拦截。

注意:

Rate Limiting 不是第一下就拦截;
它会先放行并计数;
超过阈值后才开始执行 Challenge 或 Block。

9. HEAD 请求问题

测试日志中可能出现:

Method: HEAD
Path: /api/test
Status: 404

如果限流规则只匹配 URI 路径,HEAD 请求也会被统计。

但如果规则额外限制了:

Method = GET

那么 HEAD 请求不会命中规则。

因此测试阶段建议不要限制 Method。


10. 后台域名防护建议

后台管理域名不建议只靠 Rate Limiting。

推荐使用:

Cloudflare Access

保护后台域名:

admin.example.com
deploy.example.com
dokploy.example.com

推荐策略:

后台管理域名:
只允许指定邮箱登录访问

CI/CD 部署入口:
使用 Cloudflare Access Service Token

这样可以避免后台直接暴露给公网扫描器。


11. CI/CD 与 Cloudflare Access

如果 CI/CD 需要访问受 Cloudflare Access 保护的部署接口,需要配置 Service Token。

请求时携带:

CF-Access-Client-Id
CF-Access-Client-Secret

示例:

curl -X POST "https://deploy.example.com/api/deploy" \
  -H "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \
  -H "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET" \
  -H "Authorization: Bearer $DEPLOY_TOKEN"

推荐保护方式:

Cloudflare Access Service Token
+
应用自身 API Token

不要直接把整个后台域名设置为 Bypass。


12. 额外防护建议

12.1 删除无用 DNS 记录

不再使用的子域名应删除 DNS 记录或 Tunnel Public Hostname。

12.2 API 接口增加认证

不要只依赖 Cloudflare,后端接口仍然需要:

登录认证
权限校验
Token 校验
参数校验

12.3 登录/验证码接口单独限流

登录、注册、发送验证码接口建议更严格:

5 次 / 1 分钟

例如:

/api/user/login
/api/user/register
/api/user/send-code

12.4 常见扫描路径直接 Block

可以在 Cloudflare WAF Custom Rules 中直接拦截:

/.env
/wp-
/phpmyadmin
/api/app/getserviceinfo

示例表达式:

(
  http.request.uri.path contains "/.env"
  or http.request.uri.path contains "/wp-"
  or http.request.uri.path contains "/phpmyadmin"
  or http.request.uri.path contains "/api/app/getserviceinfo"
)

动作:

Block

13. 最终结论

本次问题的核心原因是:

服务器关闭 80/443 只能阻止外部直接访问源站端口;
如果仍然存在 Cloudflare Tunnel、反向代理或域名入口,请求仍然可以进入应用;
Cloudflare 默认不会拦截所有普通路径扫描;
需要手动配置 Rate Limiting、WAF Custom Rules 和 Access。

本次已验证:

Cloudflare Rate Limiting 规则有效;
当请求频率超过设置阈值后,会触发 Cloudflare 拦截;
应用日志中不再持续收到超过阈值后的请求。

推荐长期配置:

API 域名:
Rate Limiting + WAF Custom Rules + 后端认证

后台域名:
Cloudflare Access + 指定邮箱访问

CI/CD:
Cloudflare Access Service Token + 应用部署 Token

源站服务器:
安全组只开放必要端口,SSH 仅允许白名单 IP