关于 cdn-setup
cdn-setup 是面向配置 CDN 的技能,涵盖 CloudFront 与 Cloudflare 的部署、缓存和排查,适用于加速网站及 API 内容交付。
主要功能
- 01配置 CloudFront 与 Cloudflare CDN
- 02设置缓存规则和源站缓存头
- 03按路径清除边缘缓存
- 04使用 Terraform 管理配置
- 05检查缓存表现并排查问题
适用场景
- 01为全球用户加速静态资源
- 02通过 CDN 缓存 API 响应
- 03部署后清除过期对象缓存
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ bagelhole/ devops-security-agent-skills --skill cdn-setup安装后在 Claude Code 中输入/cdn-setup确认已加载。
详细介绍
CDN 设置
配置内容分发网络,以通过适当的缓存、失效处理和安全措施,实现快速、可靠的全球资源分发。
适用场景
- 在全球范围内以低延迟提供静态资源(JS、CSS、图片、字体)。
- 将流量从源服务器分流,以降低计算成本。
- 在边缘节点添加 TLS 终止和 DDoS 防护。
- 实施基于地理位置的路由或内容限制。
- 通过边缘缓存加速 API 响应。
前提条件
- 可管理 DNS 的域名。
- 提供待分发内容的源服务器或 S3/R2 存储桶。
- 已配置 AWS CLI(用于 CloudFront)。
- 已配置区域的 Cloudflare 账户(用于 Cloudflare CDN)。
- Terraform 1.5+(用于基础设施即代码示例)。
AWS CloudFront
通过 CLI 创建分配
# Create an S3 origin distribution with OAC (Origin Access Control)
aws cloudfront create-distribution --distribution-config '{
"CallerReference": "my-site-'$(date +%s)'",
"Comment": "Production site CDN",
"Enabled": true,
"Origins": {
"Quantity": 1,
"Items": [{
"Id": "s3-origin",
"DomainName": "my-bucket.s3.us-east-1.amazonaws.com",
"OriginPath": "",
"S3OriginConfig": {
"OriginAccessIdentity": ""
},
"OriginAccessControlId": "E2QWRUHAPOMQZL"
}]
},
"DefaultCacheBehavior": {
"TargetOriginId": "s3-origin",
"ViewerProtocolPolicy": "redirect-to-https",
"AllowedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"]
},
"CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6",
"Compress": true
},
"DefaultRootObject": "index.html",
"PriceClass": "PriceClass_100",
"ViewerCertificate": {
"ACMCertificateArn": "arn:aws:acm:us-east-1:123456789:certificate/abc-123",
"SSLSupportMethod": "sni-only",
"MinimumProtocolVersion": "TLSv1.2_2021"
},
"Aliases": {
"Quantity": 1,
"Items": ["www.example.com"]
},
"CustomErrorResponses": {
"Quantity": 1,
"Items": [{
"ErrorCode": 404,
"ResponseCode": "200",
"ResponsePagePath": "/index.html",
"ErrorCachingMinTTL": 10
}]
}
}'
缓存失效处理
# Invalidate specific paths
aws cloudfront create-invalidation \
--distribution-id E1A2B3C4D5E6F7 \
--paths "/index.html" "/css/*" "/js/*"
# Invalidate everything (costs apply per path)
aws cloudfront create-invalidation \
--distribution-id E1A2B3C4D5E6F7 \
--paths "/*"
# Check invalidation status
aws cloudfront get-invalidation \
--distribution-id E1A2B3C4D5E6F7 \
--id I1A2B3C4D5E6F7
# List recent invalidations
aws cloudfront list-invalidations --distribution-id E1A2B3C4D5E6F7
CloudFront Functions(轻量级边缘逻辑)
// URL rewrite function — add index.html to directory requests
function handler(event) {
var request = event.request;
var uri = request.uri;
if (uri.endsWith('/')) {
request.uri += 'index.html';
} else if (!uri.includes('.')) {
request.uri += '/index.html';
}
return request;
}
使用 Terraform 配置 CloudFront
# cloudfront.tf
resource "aws_cloudfront_distribution" "site" {
enabled = true
is_ipv6_enabled = true
default_root_object = "index.html"
aliases = ["www.example.com"]
price_class = "PriceClass_100"
origin {
domain_name = aws_s3_bucket.site.bucket_regional_domain_name
origin_id = "s3-origin"
origin_access_control_id = aws_cloudfront_origin_access_control.oac.id
}
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "s3-origin"
cache_policy_id = "658327ea-f89d-4fab-a63d-7e88639e58f6" # CachingOptimized
origin_request_policy_id = "88a5eaf4-2fd4-4709-b370-b4c650ea3fcf" # CORS-S3Origin
viewer_protocol_policy = "redirect-to-https"
compress = true
}
# SPA fallback
custom_error_response {
error_code = 404
response_code = 200
response_page_path = "/index.html"
}
# API pass-through (no caching)
ordered_cache_behavior {
path_pattern = "/api/*"
allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "api-origin"
cache_policy_id = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad" # CachingDisabled
origin_request_policy_id = "b689b0a8-53d0-40ab-baf2-68738e2966ac" # AllViewerExceptHostHeader
viewer_protocol_policy = "https-only"
}
viewer_certificate {
acm_certificate_arn = aws_acm_certificate.cert.arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
}
resource "aws_cloudfront_origin_access_control" "oac" {
name = "s3-oac"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}
Cloudflare CDN
区域设置
# Add a zone
curl -X POST "https://api.cloudflare.com/client/v4/zones" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"example.com","jump_start":true}'
# Get zone ID
ZONE_ID=$(curl -s "https://api.cloudflare.com/client/v4/zones?name=example.com" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq -r '.result[0].id')
缓存规则(替代 Page Rules)
# Create a cache rule for static assets
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets/phases/http_request_cache_settings/entrypoint" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"rules": [
{
"expression": "(http.request.uri.path.extension in {\"css\" \"js\" \"png\" \"jpg\" \"woff2\" \"svg\"})",
"action": "set_cache_settings",
"action_parameters": {
"cache": true,
"browser_ttl": { "mode": "override_origin", "default": 2592000 },
"edge_ttl": { "mode": "override_origin", "default": 86400 }
},
"description": "Cache static assets aggressively"
}
]
}'
清除缓存
# Purge everything
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"purge_everything":true}'
# Purge specific URLs
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"files":["https://example.com/style.css","https://example.com/app.js"]}'
# Purge by prefix
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"prefixes":["https://example.com/images/"]}'
源站的缓存标头
nginx 缓存标头
# Immutable hashed assets (fingerprinted filenames)
location ~* \.(js|css)$ {
if ($uri ~* "\.[a-f0-9]{8,}\.(js|css)$") {
expires 1y;
add_header Cache-Control "public, immutable";
}
expires 7d;
add_header Cache-Control "public, must-revalidate";
}
# Images and fonts
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|woff2|ttf)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# HTML — always revalidate
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, must-revalidate";
}
# API responses — no caching
location /api/ {
add_header Cache-Control "no-store, no-cache";
add_header Vary "Authorization, Accept";
}
Cache-Control 速查表
| 标头 | 含义 |
|---|---|
public, max-age=31536000, immutable | 缓存 1 年,永不重新验证(带哈希的资源) |
public, max-age=86400, must-revalidate | 缓存 1 天,之后检查内容是否为最新 |
private, max-age=600 | 仅浏览器缓存,缓存 10 分钟(用户专属内容) |
no-cache | 提供内容前始终向源站重新验证 |
no-store | 永不缓存(敏感数据) |
s-maxage=3600 | CDN 缓存 1 小时;对共享缓存而言,此值优先于 max-age |
缓存预热
# Warm cache for critical pages after deployment
#!/bin/bash
URLS=(
"https://www.example.com/"
"https://www.example.com/products"
"https://www.example.com/about"
"https://www.example.com/css/main.abc123.css"
"https://www.example.com/js/app.def456.js"
)
for url in "${URLS[@]}"; do
curl -s -o /dev/null -w "%{http_code} %{time_total}s %{url_effective}\n" "$url"
done
监控缓存性能
# Check cache status from response headers
curl -sI https://www.example.com/style.css | grep -i -E "cf-cache|x-cache|age|cache-control"
# Expected headers:
# cf-cache-status: HIT (Cloudflare)
# x-cache: Hit from cloudfront (CloudFront)
# age: 3600 (seconds since cached)
# CloudFront cache hit ratio
aws cloudwatch get-metric-statistics \
--namespace AWS/CloudFront \
--metric-name CacheHitRate \
--dimensions Name=DistributionId,Value=E1A2B3C4D5E6F7 \
--start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
--period 300 \
--statistics Average
故障排查
| 症状 | 原因 | 解决方法 |
|---|---|---|
cf-cache-status: DYNAMIC | 没有匹配的缓存规则,或 Cache-Control 禁止缓存 | 设置 s-maxage,或为该路径创建缓存规则 |
| 缓存命中率低于 50% | TTL 较短,或 URL 基数较高(查询字符串) | 增加 TTL;从缓存键中移除不必要的查询字符串 |
| 部署后内容仍然过期 | 旧对象仍缓存在边缘节点 | 执行失效处理;使用内容哈希文件名可彻底避免此问题 |
| 通过 CDN 访问时出现 CORS 错误 | CDN 移除了 Vary 标头,或缓存了错误的标头 | 添加 Vary: Origin,并配置源站请求策略以转发 Origin |
| CDN 返回 502 错误 | 源站宕机或超时 | 检查源站运行状况;增加 CDN 源站超时设置 |
| 出现混合内容警告 | CDN 使用 HTTPS 提供内容,但源站链接使用 HTTP | 设置 viewer-protocol-policy: redirect-to-https;修正源站 URL |
| 失效处理成本高 | 每次部署都清除 /* | 使用指纹文件名;仅对 index.html 执行失效处理 |
相关技能
- dns-management - 用于设置 CDN CNAME 的 DNS 记录
- cloudflare-pages - Cloudflare 为 Pages 项目提供的内置 CDN
- reverse-proxy - CDN 后方的源服务器配置
- load-balancing - 多源站 CDN 后端
评论
支持 Markdown