用开源组件搭一套“私有版 Cloudflare”:网关、WAF、缓存与源码实战
Cloudflare 私有化部署方案|附源码
说明:严格来说,Cloudflare 本身是一个全球化的 SaaS / 边缘网络平台,并不提供“官方私有化部署版”。因此,本文所说的“Cloudflare 私有化部署方案”,并不是把 Cloudflare 原厂服务部署到自己的服务器上,而是基于开源组件,构建一套具备 CDN 加速、反向代理、HTTPS、WAF、安全访问控制、缓存、日志监控、源站隐藏 等能力的私有化替代方案。
一、为什么需要 Cloudflare 私有化部署方案?
Cloudflare 是很多网站和业务系统常用的前置安全与加速平台,常见能力包括:
- DNS 解析;
- CDN 静态资源加速;
- HTTPS 证书托管;
- WAF Web 应用防火墙;
- DDoS 缓解;
- 反向代理;
- Bot 防护;
- Zero Trust 访问控制;
- 边缘缓存;
- 访问日志与安全分析。
对于中小型业务来说,Cloudflare 的托管方式非常方便。但在某些场景下,企业可能希望构建一套可私有化部署的替代方案。
常见原因包括:
-
数据合规要求
某些行业对数据流转、访问日志、用户 IP、请求内容有严格要求,不希望流量经过第三方平台。 -
内网系统访问控制
企业内部系统可能部署在私有云、IDC 或 Kubernetes 集群内,需要通过统一入口进行安全访问。 -
源站隐藏与安全隔离
不希望源站 IP 暴露在公网,需要通过网关层统一代理访问。 -
可控性更强
希望自定义缓存策略、限流策略、WAF 规则、认证逻辑、日志采集方式。 -
成本与架构自主性
对于大流量业务,长期依赖商业 CDN 可能成本较高,私有化方案可以按需扩展。
二、方案目标
本文设计的私有化方案,将实现类似 Cloudflare 的部分核心能力。
目标能力
| 能力 | 实现方式 |
|---|---|
| 反向代理 | Nginx / OpenResty |
| HTTPS | Caddy / Nginx + Certbot |
| 缓存加速 | Nginx Proxy Cache |
| WAF | ModSecurity + OWASP CRS |
| 访问限流 | Nginx limit_req |
| IP 黑白名单 | Nginx / OpenResty |
| 安全 Header | Nginx 配置 |
| 日志采集 | Filebeat / Prometheus / Grafana |
| 容器化部署 | Docker Compose |
| 多源站转发 | Nginx upstream |
| 健康检查 | Nginx upstream + Docker healthcheck |
| 简易管理接口 | Node.js API 示例 |
三、整体架构设计
整体架构如下:
用户浏览器
|
| HTTPS
v
私有化边缘网关
Nginx / OpenResty / Caddy
|
| WAF / 限流 / 缓存 / 日志
v
业务源站服务
Web / API / 静态资源 / Kubernetes Service
如果部署在多节点环境中,可以扩展为:
用户
|
DNS 解析
|
负载均衡 SLB / LVS / Keepalived
|
多个边缘代理节点
|
内部源站服务
在这个架构中,边缘网关承担类似 Cloudflare 的角色:
- 终止 HTTPS;
- 隐藏源站地址;
- 缓存静态资源;
- 过滤恶意请求;
- 限制异常访问;
- 记录访问日志;
- 将请求转发给内部源站。
四、技术选型
1. Nginx
Nginx 是高性能反向代理服务器,非常适合作为网关入口。它可以处理:
- 反向代理;
- 静态资源缓存;
- Gzip 压缩;
- 限流;
- IP 控制;
- HTTPS;
- 日志输出。
2. OpenResty
OpenResty 基于 Nginx,并集成 Lua 能力。相比原生 Nginx,OpenResty 可以实现更复杂的动态逻辑,例如:
- 动态黑名单;
- 动态路由;
- JWT 校验;
- 自定义鉴权;
- API 请求签名验证;
- Bot 识别逻辑。
3. ModSecurity
ModSecurity 是开源 WAF 引擎,配合 OWASP Core Rule Set 可以防护常见攻击:
- SQL 注入;
- XSS;
- 路径穿越;
- 命令注入;
- 恶意 User-Agent;
- 非法请求头。
4. Docker Compose
为了方便部署,本文使用 Docker Compose 编排多个服务:
- Nginx 网关;
- 示例源站服务;
- 管理 API;
- Redis;
- Prometheus;
- Grafana。
五、目录结构
项目目录如下:
cloudflare-private-demo/
├── docker-compose.yml
├── nginx/
│ ├── nginx.conf
│ ├── conf.d/
│ │ └── gateway.conf
│ ├── cache/
│ └── logs/
├── app/
│ ├── package.json
│ └── server.js
├── admin-api/
│ ├── package.json
│ └── server.js
└── README.md
六、Docker Compose 部署源码
下面是完整的 docker-compose.yml 示例。
version: "3.8"
services:
nginx-gateway:
image: nginx:1.25
container_name: private-cloudflare-gateway
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/cache:/var/cache/nginx
- ./nginx/logs:/var/log/nginx
depends_on:
- origin-app
- admin-api
networks:
- edge-net
origin-app:
build:
context: ./app
container_name: origin-app
restart: always
networks:
- edge-net
admin-api:
build:
context: ./admin-api
container_name: admin-api
restart: always
environment:
- ADMIN_TOKEN=change-me-secret-token
networks:
- edge-net
networks:
edge-net:
driver: bridge
七、Nginx 主配置源码
创建文件:nginx/nginx.conf
user nginx;
worker_processes auto;
events {
worker_connections 4096;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server_tokens off;
log_format edge_log '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time '
'uct=$upstream_connect_time '
'uht=$upstream_header_time '
'urt=$upstream_response_time '
'cache=$upstream_cache_status';
access_log /var/log/nginx/access.log edge_log;
error_log /var/log/nginx/error.log warn;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
client_max_body_size 20m;
gzip on;
gzip_comp_level 5;
gzip_min_length 1k;
gzip_types
text/plain
text/css
application/json
application/javascript
text/xml
application/xml
image/svg+xml;
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=edge_cache:100m
max_size=2g
inactive=60m
use_temp_path=off;
limit_req_zone $binary_remote_addr zone=req_limit_zone:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn_limit_zone:10m;
include /etc/nginx/conf.d/*.conf;
}
八、网关配置源码
创建文件:nginx/conf.d/gateway.conf
upstream origin_backend {
server origin-app:3000 max_fails=3 fail_timeout=10s;
keepalive 32;
}
upstream admin_backend {
server admin-api:4000 max_fails=3 fail_timeout=10s;
keepalive 16;
}
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
limit_req zone=req_limit_zone burst=30 nodelay;
limit_conn conn_limit_zone 50;
location = /health {
access_log off;
return 200 "ok\n";
}
location /static/ {
proxy_pass http://origin_backend;
proxy_cache edge_cache;
proxy_cache_valid 200 301 302 30m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Edge-Cache $upstream_cache_status always;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/ {
proxy_pass http://origin_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 3s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
location /admin/ {
proxy_pass http://admin_backend/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://origin_backend;
proxy_cache edge_cache;
proxy_cache_valid 200 10m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Edge-Cache $upstream_cache_status always;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
注意:如果你暂时没有 HTTPS 证书,可以先将 443 配置注释,仅使用 80 端口测试。生产环境建议使用 Let’s Encrypt、内部 CA 或云厂商证书。
九、源站应用源码
创建目录:app/
app/package.json
{
"name": "origin-app",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.3"
}
}
app/server.js
const express = require("express");
const app = express();
const port = 3000;
app.get("/", function (req, res) {
res.send(`
Private Cloudflare Demo
Cloudflare 私有化替代方案演示
当前请求已经经过私有边缘网关代理。
真实客户端 IP:${req.headers["x-real-ip"] || req.ip}
`);
});
app.get("/api/hello", function (req, res) {
res.json({
message: "hello from origin app",
time: new Date().toISOString(),
realIp: req.headers["x-real-ip"] || req.ip,
forwardedFor: req.headers["x-forwarded-for"] || null
});
});
app.get("/static/app.js", function (req, res) {
res.type("application/javascript");
res.send(`
console.log("static file from origin app");
`);
});
app.listen(port, function () {
console.log(`origin app listening on port ${port}`);
});
十、管理 API 源码
这个管理 API 用于模拟私有网关的后台接口。真实生产环境中,可以将它扩展为:
- 动态黑名单管理;
- 域名配置管理;
- 缓存刷新;
- WAF 规则管理;
- 访问统计查询;
- 审计日志查询。
创建目录:admin-api/
admin-api/package.json
{
"name": "admin-api",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.3"
}
}
admin-api/server.js
const express = require("express");
const app = express();
const port = 4000;
app.use(express.json());
function auth(req, res, next) {
const token = req.headers["x-admin-token"];
const expected = process.env.ADMIN_TOKEN || "change-me-secret-token";
if (token !== expected) {
return res.status(401).json({
error: "unauthorized"
});
}
next();
}
let blacklist = [];
app.get("/health", function (req, res) {
res.json({
status: "ok"
});
});
app.get("/blacklist", auth, function (req, res) {
res.json({
blacklist
});
});
app.post("/blacklist", auth, function (req, res) {
const ip = req.body.ip;
if (!ip) {
return res.status(400).json({
error: "ip is required"
});
}
if (!blacklist.includes(ip)) {
blacklist.push(ip);
}
res.json({
message: "ip added",
blacklist
});
});
app.delete("/blacklist/:ip", auth, function (req, res) {
const ip = req.params.ip;
blacklist = blacklist.filter(item => item !== ip);
res.json({
message: "ip removed",
blacklist
});
});
app.listen(port, function () {
console.log(`admin api listening on port ${port}`);
});
十一、Dockerfile 源码
分别在 app/ 和 admin-api/ 目录下创建相同的 Dockerfile。
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --production
COPY server.js ./
EXPOSE 3000
CMD ["npm", "start"]
对于 admin-api,由于端口是 4000,可以将 Dockerfile 改成:
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --production
COPY server.js ./
EXPOSE 4000
CMD ["npm", "start"]
十二、启动部署
在项目根目录执行:
docker compose up -d --build
查看容器状态:
docker compose ps
查看网关日志:
docker logs private-cloudflare-gateway
访问测试:
curl http://localhost
如果启用了 HTTPS,并配置了证书,可以访问:
curl -k https://example.com
测试 API:
curl http://localhost/api/hello
测试静态资源缓存:
curl -I http://localhost/static/app.js
如果返回头中出现:
X-Edge-Cache: MISS
再次请求可能变为:
X-Edge-Cache: HIT
说明缓存已经生效。
十三、加入基础 WAF 能力
如果需要实现更接近 Cloudflare WAF 的能力,可以使用 ModSecurity。
示例 Docker 镜像可以选择:
owasp/modsecurity-crs
或者使用 OpenResty + Lua 自行编写规则。
简单的 Nginx 黑名单规则示例:
map $remote_addr $blocked_ip {
default 0;
192.168.1.100 1;
10.10.10.10 1;
}
server {
if ($blocked_ip) {
return 403;
}
}
限制恶意 User-Agent:
map $http_user_agent $bad_bot {
default 0;
~*curl 1;
~*python-requests 1;
~*sqlmap 1;
~*nikto 1;
}
server {
if ($bad_bot) {
return 403;
}
}
不过需要注意,基于 User-Agent 的拦截只能作为辅助策略,因为 User-Agent 很容易伪造。生产环境中,更可靠的方式是结合:
- 请求频率;
- IP 信誉;
- Cookie;
- JS Challenge;
- 行为特征;
- 登录状态;
- 风险评分。
十四、缓存策略设计
缓存是 Cloudflare 的重要能力之一。私有化方案中,可以通过 Nginx Proxy Cache 实现。
推荐策略如下:
| 资源类型 | 缓存时间 | 说明 |
|---|---|---|
| HTML | 1-10 分钟 | 视业务动态程度而定 |
| CSS / JS | 7-30 天 | 文件名带 hash 时可长缓存 |
| 图片 | 7-30 天 | 适合边缘缓存 |
| API GET | 10 秒-5 分钟 | 只缓存幂等接口 |
| API POST | 不缓存 | 避免业务错误 |
| 登录态页面 | 不缓存 | 防止数据泄露 |
Nginx 中可以通过如下配置避免缓存登录用户请求:
set $skip_cache 0;
if ($request_method = POST) {
set $skip_cache 1;
}
if ($http_authorization != "") {
set $skip_cache 1;
}
if ($http_cookie ~* "session|token|auth") {
set $skip_cache 1;
}
proxy_no_cache $skip_cache;
proxy_cache_bypass $skip_cache;
十五、源站隐藏方案
如果要达到类似 Cloudflare 的源站隐藏效果,关键点是:源站不应该直接暴露在公网。
推荐方式:
- 源站服务只监听内网 IP;
- 安全组只允许边缘网关访问源站端口;
- 业务域名只解析到边缘网关;
- 源站服务禁止公网访问;
- 使用内网 DNS 或服务发现连接源站。
例如,在云服务器安全组中配置:
源站 3000 端口:
允许:边缘网关内网 IP
拒绝:所有公网 IP
这样即使攻击者知道源站端口,也无法绕过网关直接访问源站。
十六、日志与监控
私有化网关必须具备日志与监控能力,否则安全事件很难追踪。
建议采集以下指标:
- QPS;
- 请求状态码;
- 请求耗时;
- 上游响应时间;
- 缓存命中率;
- 单 IP 请求频率;
- 4xx / 5xx 错误比例;
- WAF 拦截数量;
- 带宽使用量;
- TLS 握手情况。
Nginx 日志可以接入:
- Filebeat + Elasticsearch + Kibana;
- Promtail + Loki + Grafana;
- OpenTelemetry Collector;
- Prometheus Exporter。
如果只做简单分析,可以直接使用:
tail -f nginx/logs/access.log
统计访问最多的 IP:
awk '{print $1}' nginx/logs/access.log | sort | uniq -c | sort -nr | head
统计状态码:
awk '{print $9}' nginx/logs/access.log | sort | uniq -c | sort -nr
十七、生产环境优化建议
如果要将该方案用于生产,需要进一步完善以下方面。
1. 高可用
单台网关存在单点故障,建议至少部署两台以上,通过:
- SLB;
- LVS;
- Keepalived;
- BGP Anycast;
- DNS 轮询;
实现高可用。
2. 配置热更新
Nginx 支持平滑重载:
nginx -s reload
容器环境中可以执行:
docker exec private-cloudflare-gateway nginx -s reload
3. 动态规则
如果需要动态黑名单、动态缓存刷新、动态路由,可以考虑:
- OpenResty + Redis;
- Envoy + xDS;
- Traefik;
- Kong Gateway;
- APISIX。
其中 Apache APISIX 对插件化、动态配置、API 网关能力支持较好,也适合企业级私有化网关。
4. 证书自动化
HTTPS 证书可以使用:
- Let’s Encrypt;
- acme.sh;
- Certbot;
- Caddy 自动签发;
- 内部 CA。
推荐使用 acme.sh 自动续期:
acme.sh --issue -d example.com --standalone
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/certs/privkey.pem \
--fullchain-file /etc/nginx/certs/fullchain.pem \
--reloadcmd "docker exec private-cloudflare-gateway nginx -s reload"
5. 安全加固
需要重点关注:
- 禁止暴露源站;
- 禁止暴露管理 API;
- 管理 API 必须鉴权;
- 日志不能记录敏感 Token;
- 证书私钥权限最小化;
- 定期更新镜像;
- WAF 规则定期更新;
- 对异常 IP 自动封禁;
- 对登录接口做更严格限流。
十八、与 Cloudflare 的差异
私有化替代方案虽然可以实现部分功能,但与 Cloudflare 仍然存在明显差异。
| 对比项 | Cloudflare | 私有化方案 |
|---|---|---|
| 全球 CDN 节点 | 原生具备 | 需要自建 |
| DDoS 清洗 | 强 | 依赖带宽和上游 |
| WAF | 成熟规则库 | 需自行维护 |
| 运维成本 | 低 | 中高 |
| 数据可控性 | 较弱 | 强 |
| 定制能力 | 有限制 | 强 |
| 部署复杂度 | 低 | 中高 |
| 内网集成 | 一般 | 强 |
因此,私有化方案更适合以下场景:
- 企业内部系统;
- 专有云环境;
- 合规要求较高业务;
- 访问规模可控的网站;
- 需要深度定制网关逻辑的系统。
如果是面向全球公网的大规模业务,仍然建议结合专业 CDN、云安全厂商和 DDoS 清洗服务共同使用。
十九、完整部署命令汇总
mkdir -p cloudflare-private-demo
cd cloudflare-private-demo
mkdir -p nginx/conf.d nginx/cache nginx/logs app admin-api
创建上述配置文件后,执行:
docker compose up -d --build
测试访问:
curl http://localhost
curl http://localhost/api/hello
curl -I http://localhost/static/app.js
查看日志:
tail -f nginx/logs/access.log
停止服务:
docker compose down
清理缓存:
rm -rf nginx/cache/*
二十、总结
Cloudflare 无法真正被“私有化部署”,但可以通过 Nginx、OpenResty、ModSecurity、Docker、Redis、Prometheus 等开源组件,构建一套具备 Cloudflare 核心能力的私有边缘网关方案。
本文提供的方案可以实现:
- HTTPS 入口;
- 反向代理;
- 源站隐藏;
- 静态资源缓存;
- 访问限流;
- 基础安全 Header;
- 日志记录;
- 管理 API;
- 容器化部署。
对于企业内部系统、私有云业务、合规要求较高的系统,这类方案具备较高的实用价值。但在面对大规模公网攻击、全球加速、复杂 Bot 对抗时,自建方案仍然无法完全替代 Cloudflare,需要结合云厂商安全产品、专业 CDN 和 DDoS 清洗能力共同使用。
如果你只是希望拥有一个可控、可扩展、可二次开发的私有网关,那么本文给出的源码和架构可以作为一个基础版本。后续可以继续扩展动态配置、WAF 插件、Redis 黑名单、Prometheus 监控、Grafana 可视化、自动证书续期和多节点高可用,从而逐步演进为一套真正适合企业使用的私有化边缘安全平台。