Cloudflare 不止 CDN:从加速、防刷到文件存储的实战代码指南
Cloudflare 实战案例分享|附源码
在很多团队的技术架构中,Cloudflare 往往最初只是被当作一个“DNS 托管 + CDN 加速”工具使用。但随着 Cloudflare 产品体系不断完善,它已经逐渐演变成一套完整的边缘计算平台:DNS、CDN、WAF、Workers、KV、R2、Pages、Turnstile、Zero Trust 等能力组合起来,可以帮助我们在不增加太多服务器成本的情况下,完成安全防护、接口代理、静态资源加速、无服务器 API、对象存储、访问控制等多种场景。
本文将结合几个真实开发中非常常见的场景,分享 Cloudflare 的实战用法,并附上可直接参考的源码示例。文章重点不是罗列产品功能,而是从实际问题出发,讲清楚为什么使用 Cloudflare、如何设计方案,以及代码如何落地。
一、案例背景:为什么选择 Cloudflare?
假设我们有一个中小型 Web 项目,架构大致如下:
- 前端项目使用 Vue、React 或 Next.js 构建;
- 后端接口部署在一台云服务器上;
- 图片、文件等静态资源放在对象存储或服务器目录中;
- 域名通过普通 DNS 服务解析;
- 服务器暴露公网 IP;
- 接口没有复杂的安全防护;
- 海外访问速度一般;
- 偶尔会遭遇恶意扫描、爬虫请求或简单的 CC 攻击。
这种架构在项目早期很常见,但随着访问量增加,会逐渐暴露一些问题:
-
静态资源访问慢
图片、JS、CSS 都从源站加载,用户距离服务器越远,访问延迟越明显。 -
源站 IP 容易暴露
一旦攻击者拿到服务器真实 IP,就可以绕过 CDN 直接攻击源站。 -
服务器承担过多职责
静态资源、接口转发、鉴权逻辑、缓存控制都放在服务器上,维护成本较高。 -
安全防护能力弱
普通服务器很难低成本处理恶意请求、机器人访问和高频扫描。 -
弹性能力不足
突发流量可能让服务器带宽或 CPU 瞬间升高。
Cloudflare 的优势在于:它处在用户和源站之间,可以在边缘节点完成大量工作,例如缓存、拦截、重写请求、鉴权、代理接口、隐藏源站等。合理使用 Cloudflare,可以让源站压力显著降低,同时提升访问速度和安全性。
二、案例一:使用 Cloudflare CDN 加速静态网站
1. 场景说明
我们有一个静态前端项目,例如 Vue 或 React 打包后的文件:
dist/
├── index.html
├── assets/
│ ├── app.8a9f3.js
│ ├── style.7c2d1.css
│ └── logo.png
这些文件可以部署到:
- Cloudflare Pages;
- GitHub Pages;
- 自有服务器 Nginx;
- S3/R2 等对象存储。
如果使用 Cloudflare Pages,配置最简单;如果使用自有服务器,也可以通过 Cloudflare CDN 进行缓存加速。
2. 推荐缓存策略
静态网站通常需要区分两类文件:
-
index.html
不建议长期缓存,因为它负责引用最新的 JS/CSS 文件。若缓存太久,用户可能一直访问旧版本。 -
带 hash 的静态资源
例如app.8a9f3.js、style.7c2d1.css。这类文件名变化即代表内容变化,可以长期缓存。
推荐策略:
index.html: Cache-Control: no-cache
/assets/*: Cache-Control: public, max-age=31536000, immutable
3. Nginx 示例配置
如果前端文件部署在自有服务器上,可以这样配置:
server {
listen 80;
server_name example.com;
root /var/www/example/dist;
index index.html;
location = /index.html {
add_header Cache-Control "no-cache";
try_files $uri =404;
}
location /assets/ {
add_header Cache-Control "public, max-age=31536000, immutable";
try_files $uri =404;
}
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache";
}
}
然后在 Cloudflare 中开启橙色云朵代理,并进入:
Caching -> Configuration -> Caching Level
设置为标准缓存即可。
4. 实战建议
对于静态网站,我建议优先使用 Cloudflare Pages,原因如下:
- 免费额度足够个人项目和中小项目使用;
- 自动 HTTPS;
- 自动 CDN;
- 支持 GitHub/GitLab 自动部署;
- 支持 Preview 分支预览;
- 可以结合 Functions 编写简单接口。
如果项目只是一个官网、文档站、落地页、博客,Cloudflare Pages 基本可以替代传统服务器部署。
三、案例二:使用 Cloudflare Workers 实现 API 代理
1. 场景说明
很多前端项目会遇到跨域问题,或者后端接口不希望直接暴露给浏览器。例如:
前端页面:https://www.example.com
后端接口:https://api-origin.example.net
我们希望用户请求:
https://www.example.com/api/user
实际由 Cloudflare Worker 转发到:
https://api-origin.example.net/user
这样可以带来几个好处:
- 隐藏真实 API 地址;
- 统一处理 CORS;
- 在边缘层做简单鉴权;
- 增加缓存;
- 对非法请求提前拦截;
- 减少源站暴露面。
2. Worker 代理源码
下面是一个基础的 Cloudflare Worker API 代理示例。
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// 只允许代理 /api/ 开头的路径
if (!url.pathname.startsWith('/api/')) {
return new Response('Not Found', { status: 404 });
}
// 目标源站地址
const upstream = 'https://api-origin.example.net';
// 去掉 /api 前缀
const targetPath = url.pathname.replace(/^\/api/, '');
const targetUrl = new URL(upstream + targetPath);
targetUrl.search = url.search;
// 复制请求头
const headers = new Headers(request.headers);
// 可选:隐藏部分敏感头
headers.delete('cf-connecting-ip');
headers.delete('cf-ipcountry');
// 设置真实 Host
headers.set('Host', new URL(upstream).host);
const proxyRequest = new Request(targetUrl.toString(), {
method: request.method,
headers,
body: ['GET', 'HEAD'].includes(request.method) ? undefined : request.body,
redirect: 'follow'
});
const response = await fetch(proxyRequest);
// 复制响应,方便修改响应头
const newHeaders = new Headers(response.headers);
// 统一 CORS
newHeaders.set('Access-Control-Allow-Origin', 'https://www.example.com');
newHeaders.set('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
newHeaders.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders
});
}
};
3. 增加 OPTIONS 预检处理
实际项目中,如果前端使用 Authorization 或自定义请求头,浏览器通常会发送 OPTIONS 预检请求。我们可以在 Worker 中提前返回:
function handleOptions() {
return new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': 'https://www.example.com',
'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400'
}
});
}
export default {
async fetch(request, env, ctx) {
if (request.method === 'OPTIONS') {
return handleOptions();
}
return new Response('Hello Worker');
}
};
在生产环境中,应避免使用:
Access-Control-Allow-Origin: *
尤其是涉及用户身份、Cookie、Token 的接口时,更应该明确允许的域名。
四、案例三:在边缘层做简单防刷和访问限制
1. 场景说明
有些接口不复杂,但很容易被刷,例如:
- 发送短信验证码;
- 提交留言;
- 登录接口;
- 搜索接口;
- 下载接口;
- 抽奖接口。
如果所有请求都打到源站,源站需要承担大量无效流量。我们可以在 Cloudflare Worker 中先做一层简单限流。
Cloudflare 本身也提供 WAF、Rate Limiting 等功能,但对于个性化规则,Worker 更灵活。
2. 基于 KV 的 IP 限流源码
下面示例实现一个简单规则:
同一个 IP 在 60 秒内最多请求 20 次。
需要先创建一个 KV 命名空间,例如:
wrangler kv namespace create RATE_LIMIT_KV
然后在 wrangler.toml 中绑定:
name = "rate-limit-worker"
main = "src/index.js"
compatibility_date = "2024-01-01"
kv_namespaces = [
{ binding = "RATE_LIMIT_KV", id = "你的KV命名空间ID" }
]
Worker 源码如下:
const WINDOW_SECONDS = 60;
const MAX_REQUESTS = 20;
export default {
async fetch(request, env, ctx) {
const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
const key = `rate:${ip}`;
const current = await env.RATE_LIMIT_KV.get(key, 'json');
const now = Math.floor(Date.now() / 1000);
let record = current || {
count: 0,
resetAt: now + WINDOW_SECONDS
};
// 窗口过期后重置
if (now > record.resetAt) {
record = {
count: 0,
resetAt: now + WINDOW_SECONDS
};
}
record.count += 1;
await env.RATE_LIMIT_KV.put(key, JSON.stringify(record), {
expirationTtl: WINDOW_SECONDS + 10
});
if (record.count > MAX_REQUESTS) {
return new Response(JSON.stringify({
code: 429,
message: 'Too many requests, please try again later.'
}), {
status: 429,
headers: {
'Content-Type': 'application/json',
'Retry-After': String(record.resetAt - now)
}
});
}
return new Response(JSON.stringify({
code: 0,
message: 'Request accepted',
count: record.count
}), {
headers: {
'Content-Type': 'application/json'
}
});
}
};
3. 注意事项
这个方案适合轻量级限流,但也有局限:
- KV 是最终一致性,不适合强一致计数;
- 高频请求下可能存在并发写入误差;
- 对严格限流需求,应使用 Durable Objects;
- 对更复杂的风控,应结合 Turnstile、WAF、Bot Fight Mode。
如果只是为了挡掉大部分低成本爬虫和脚本请求,这个方案已经足够实用。
五、案例四:使用 Cloudflare Turnstile 替代传统验证码
1. 场景说明
传统验证码经常影响用户体验。Cloudflare Turnstile 是一个更友好的验证码方案,很多情况下用户无需手动识别图片,只需要完成一次隐形或轻交互验证。
适合场景:
- 登录;
- 注册;
- 找回密码;
- 表单提交;
- 评论发布;
- 下载前验证。
2. 前端接入示例
HTML 页面中加入:
Turnstile Demo
3. Worker 后端校验源码
在 Worker 中验证 Turnstile Token:
export default {
async fetch(request, env, ctx) {
if (request.method !== 'POST') {
return new Response('Method Not Allowed', { status: 405 });
}
const body = await request.json();
const token = body.token;
const ip = request.headers.get('CF-Connecting-IP');
if (!token) {
return json({
code: 400,
message: 'Missing Turnstile token'
}, 400);
}
const formData = new FormData();
formData.append('secret', env.TURNSTILE_SECRET_KEY);
formData.append('response', token);
formData.append('remoteip', ip);
const verifyRes = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
body: formData
});
const verifyData = await verifyRes.json();
if (!verifyData.success) {
return json({
code: 403,
message: 'Human verification failed',
detail: verifyData['error-codes']
}, 403);
}
return json({
code: 0,
message: 'Submit success'
});
}
};
function json(data, status = 200) {
return new Response(JSON.stringify(data), {
status,
headers: {
'Content-Type': 'application/json'
}
});
}
环境变量可以通过 Wrangler 设置:
wrangler secret put TURNSTILE_SECRET_KEY
六、案例五:使用 R2 构建低成本文件存储
1. 场景说明
很多项目需要存储图片、附件、日志、备份文件。如果直接放服务器磁盘,会遇到几个问题:
- 服务器迁移麻烦;
- 磁盘空间有限;
- 文件访问占用源站带宽;
- 多服务共享文件困难;
- 备份和权限控制复杂。
Cloudflare R2 是兼容 S3 API 的对象存储服务,并且没有传统对象存储常见的出口流量费用,对于公网分发场景非常有吸引力。
2. Worker 上传文件到 R2
wrangler.toml 示例:
name = "r2-upload-demo"
main = "src/index.js"
compatibility_date = "2024-01-01"
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-demo-bucket"
Worker 源码:
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (request.method === 'POST' && url.pathname === '/upload') {
return handleUpload(request, env);
}
if (request.method === 'GET' && url.pathname.startsWith('/file/')) {
return handleGetFile(request, env);
}
return new Response('Not Found', { status: 404 });
}
};
async function handleUpload(request, env) {
const formData = await request.formData();
const file = formData.get('file');
if (!file) {
return json({ code: 400, message: 'No file uploaded' }, 400);
}
const filename = `${Date.now()}-${file.name}`;
await env.BUCKET.put(filename, file.stream(), {
httpMetadata: {
contentType: file.type || 'application/octet-stream'
}
});
return json({
code: 0,
message: 'Upload success',
url: `/file/${filename}`
});
}
async function handleGetFile(request, env) {
const url = new URL(request.url);
const key = decodeURIComponent(url.pathname.replace('/file/', ''));
const object = await env.BUCKET.get(key);
if (!object) {
return new Response('File Not Found', { status: 404 });
}
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set('etag', object.httpEtag);
headers.set('Cache-Control', 'public, max-age=31536000');
return new Response(object.body, {
headers
});
}
function json(data, status = 200) {
return new Response(JSON.stringify(data), {
status,
headers: {
'Content-Type': 'application/json'
}
});
}
这个示例完成了两个功能:
POST /upload:上传文件到 R2;GET /file/:filename:从 R2 读取文件并返回。
生产环境中,还需要补充鉴权、文件大小限制、文件类型白名单、文件名安全处理等逻辑。
七、案例六:隐藏源站 IP,提高安全性
1. 为什么要隐藏源站?
很多人以为接入 Cloudflare 后就安全了,但如果源站 IP 仍然暴露,攻击者可以绕过 Cloudflare,直接访问你的服务器。例如:
用户 -> Cloudflare -> 源站
攻击者 -> 源站真实 IP
这样 Cloudflare 的 WAF、DDoS 防护、访问规则都无法生效。
2. 源站防护策略
推荐做法:
- 服务器防火墙只允许 Cloudflare IP 访问 80/443
- 源站不要直接绑定公开域名
- 使用 Cloudflare Origin Certificate
- 关闭不必要的端口
- 后台管理入口增加额外鉴权
Cloudflare 官方会维护 IP 段列表:
https://www.cloudflare.com/ips/
3. Linux 防火墙示例
以下是一个简化版示例,仅演示思路。实际生产环境请先确认 SSH 不会被误封。
# 清空旧规则前务必谨慎
iptables -F
# 允许本地回环
iptables -A INPUT -i lo -j ACCEPT
# 允许已建立连接
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 允许 SSH,建议限制固定 IP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 允许 Cloudflare IPv4 访问 HTTP/HTTPS
for ip in $(curl -s https://www.cloudflare.com/ips-v4); do
iptables -A INPUT -p tcp -s $ip --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -s $ip --dport 443 -j ACCEPT
done
# 允许 Cloudflare IPv6 访问 HTTP/HTTPS
for ip in $(curl -s https://www.cloudflare.com/ips-v6); do
ip6tables -A INPUT -p tcp -s $ip --dport 80 -j ACCEPT
ip6tables -A INPUT -p tcp -s $ip --dport 443 -j ACCEPT
done
# 拒绝其他 HTTP/HTTPS 请求
iptables -A INPUT -p tcp --dport 80 -j DROP
iptables -A INPUT -p tcp --dport 443 -j DROP
如果服务器使用的是云厂商安全组,也可以直接在安全组中只放行 Cloudflare IP 段。
八、案例七:用 Worker 实现简单短链接服务
1. 场景说明
短链接服务是 Worker + KV 的经典案例。我们希望实现:
https://go.example.com/abc123
跳转到:
https://www.example.com/some/very/long/path?from=test
使用 Worker 可以避免部署后端服务,KV 用于保存短码和原链接映射。
2. 短链接 Worker 源码
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (request.method === 'POST' && url.pathname === '/create') {
return createShortUrl(request, env);
}
if (request.method === 'GET') {
return redirectShortUrl(url, env);
}
return new Response('Not Found', { status: 404 });
}
};
async function createShortUrl(request, env) {
const body = await request.json();
const target = body.url;
if (!target || !/^https?:\/\//.test(target)) {
return json({ code: 400, message: 'Invalid URL' }, 400);
}
const code = generateCode(6);
await env.SHORT_KV.put(`short:${code}`, target);
return json({
code: 0,
shortUrl: `/` + code,
target
});
}
async function redirectShortUrl(url, env) {
const code = url.pathname.replace('/', '');
if (!code) {
return new Response('Short URL Service');
}
const target = await env.SHORT_KV.get(`short:${code}`);
if (!target) {
return new Response('Short URL Not Found', { status: 404 });
}
return Response.redirect(target, 302);
}
function generateCode(length = 6) {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let result = '';
const array = new Uint8Array(length);
crypto.getRandomValues(array);
for (let i = 0; i < length; i++) {
result += chars[array[i] % chars.length];
}
return result;
}
function json(data, status = 200) {
return new Response(JSON.stringify(data), {
status,
headers: {
'Content-Type': 'application/json'
}
});
}
wrangler.toml 绑定 KV:
name = "short-url-worker"
main = "src/index.js"
compatibility_date = "2024-01-01"
kv_namespaces = [
{ binding = "SHORT_KV", id = "你的KV命名空间ID" }
]
3. 安全增强建议
短链接服务如果开放给所有用户,很容易被滥用。例如有人生成钓鱼链接、恶意下载链接。因此生产环境应至少增加:
- 创建接口鉴权;
- 目标链接黑名单;
- 域名白名单;
- 访问日志;
- 管理后台;
- 短码冲突检测;
- 访问次数统计;
- 过期时间。
例如设置 7 天过期:
await env.SHORT_KV.put(`short:${code}`, target, {
expirationTtl: 7 * 24 * 60 * 60
});
九、Cloudflare 实战部署流程
下面以 Workers 为例,给出一个完整部署流程。
1. 安装 Wrangler
npm install -g wrangler
2. 登录 Cloudflare
wrangler login
3. 创建项目
npm create cloudflare@latest my-worker
或手动创建:
mkdir my-worker
cd my-worker
npm init -y
npm install wrangler --save-dev
4. 配置 wrangler.toml
name = "my-worker"
main = "src/index.js"
compatibility_date = "2024-01-01"
5. 本地开发
npx wrangler dev
6. 部署上线
npx wrangler deploy
部署成功后,会得到类似地址:
https://my-worker.your-name.workers.dev
如果要绑定自定义域名,可以在 Cloudflare 控制台中配置 Worker Routes 或 Custom Domains。
十、生产环境最佳实践
1. 不要把密钥写死在代码里
错误示例:
const SECRET_KEY = '123456';
推荐使用:
wrangler secret put SECRET_KEY
代码中通过:
env.SECRET_KEY
读取。
2. 严格控制 CORS
生产环境尽量不要随意使用:
Access-Control-Allow-Origin: *
应该明确允许来源:
const allowOrigins = [
'https://www.example.com',
'https://admin.example.com'
];
const origin = request.headers.get('Origin');
if (allowOrigins.includes(origin)) {
headers.set('Access-Control-Allow-Origin', origin);
}
3. 合理使用缓存
对于接口缓存,可以在 Worker 中通过 Cache API 控制:
export default {
async fetch(request, env, ctx) {
const cache = caches.default;
const cacheKey = new Request(request.url, request);
let response = await cache.match(cacheKey);
if (response) {
return response;
}
response = await fetch(request);
response = new Response(response.body, response);
response.headers.set('Cache-Control', 'public, max-age=60');
ctx.waitUntil(cache.put(cacheKey, response.clone()));
return response;
}
};
注意:并不是所有接口都适合缓存。用户信息、订单数据、支付状态等强个性化数据不应被公共缓存。
4. 结合 WAF 规则
Cloudflare WAF 可以配置很多实用规则,例如:
- 拦截特定国家或地区访问;
- 拦截已知恶意 IP;
- 对后台路径增加挑战;
- 对登录接口启用托管挑战;
- 拦截异常 User-Agent;
- 限制高频请求。
例如后台路径:
URI Path starts with /admin
动作可以设置为:
Managed Challenge
这样访问后台前会先经过 Cloudflare 挑战验证。
5. 日志与监控
上线后不要只关注“能不能访问”,还要关注:
- Worker 请求数量;
- 错误率;
- 平均响应时间;
- 缓存命中率;
- WAF 拦截记录;
- R2 请求量;
- KV 读写次数;
- 源站带宽变化。
Cloudflare 控制台提供基础分析,如果需要更完整的日志,可以结合 Logpush 推送到第三方日志系统。
十一、常见问题总结
1. Cloudflare 会不会影响 SEO?
正常配置下不会。Cloudflare 只是作为 CDN 和安全代理,不会改变页面内容。需要注意的是,不要错误地对搜索引擎爬虫进行过度挑战,否则可能影响抓取。
2. 免费版够不够用?
对于个人网站、博客、小型工具站、静态官网,免费版通常够用。但如果需要更高级的 WAF、日志、团队权限、SLA 或更细粒度的企业安全能力,可能需要升级套餐。
3. Worker 能不能完全替代后端?
取决于业务复杂度。Worker 非常适合:
- API 网关;
- 轻量接口;
- 请求转发;
- Webhook 处理;
- 短链接;
- 鉴权前置;
- 缓存层;
- 边缘渲染;
- 静态站点增强。
但如果业务涉及复杂事务、大量关系型查询、长时间任务、重型计算,仍然建议配合传统后端服务。
4. KV、R2、Durable Objects 如何选择?
简单理解:
| 产品 | 适合场景 | 特点 |
|---|---|---|
| KV | 配置、映射、缓存、短链接 | 全球分发,最终一致 |
| R2 | 文件、图片、备份、附件 | 对象存储,兼容 S3 |
| Durable Objects | 强一致状态、协作、计数器 | 单对象强一致 |
| D1 | 轻量关系型数据 | SQLite 风格 |
| Queues | 异步任务 | 削峰填谷 |
十二、结语
Cloudflare 的价值不只是“给网站套一层 CDN”。在实际项目中,它更像是一套部署在全球边缘节点上的基础设施工具箱。通过 DNS、CDN、WAF、Workers、KV、R2、Turnstile 等能力组合,我们可以用较低成本解决很多常见问题:
- 静态资源加速;
- API 代理;
- 跨域处理;
- 简单限流;
- 人机验证;
- 文件存储;
- 短链接服务;
- 源站隐藏;
- 安全防护;
- 边缘缓存。
对于个人开发者和中小团队来说,Cloudflare 的最大优势是“低成本 + 快速落地”。很多原本需要单独购买服务器、配置 Nginx、部署后端服务、维护缓存和安全规则的工作,现在都可以在 Cloudflare 平台上完成。
如果你正在维护一个网站或 Web 应用,我建议至少完成以下几步:
- 将域名 DNS 托管到 Cloudflare;
- 开启 CDN 代理和 HTTPS;
- 配置合理缓存规则;
- 使用 WAF 保护后台和敏感接口;
- 用 Worker 做 API 代理或轻量逻辑;
- 用 Turnstile 保护表单;
- 将静态文件或附件迁移到 R2;
- 限制源站只允许 Cloudflare IP 访问。
做到这些后,你的网站在速度、安全性和可维护性上都会有明显提升。Cloudflare 不一定能替代所有后端基础设施,但它非常适合作为现代 Web 项目的第一层入口和边缘能力平台。合理使用它,往往能用很少的代码和成本,获得非常可观的工程收益。