Route53 + CloudFront 301 重定向方案

方案概述

使用 CloudFront Function 在边缘节点直接返回 301 重定向,无需源站。适用于任何域名重定向场景,包括:

  • 根域名 → www 子域名(如 example.comwww.example.com
  • 子域名 → 子域名(如 old.example.comnew.example.com
  • 跨域名重定向(如 old-domain.comnew-domain.com

关键要点

  1. 无需源站: CloudFront Function 在边缘直接返回 301,流量不会到达源站
  2. 成本低: 只有 CloudFront Function 调用费用($0.10/百万次请求)
  3. 延迟低: 在全球边缘节点执行,响应速度快
  4. SSL 必需: 自定义域名必须配置 ACM 证书(us-east-1 区域)
  5. 根域名支持: 完全支持根域名(apex domain)重定向
  6. SEO 友好: 301 永久重定向,搜索引擎会更新索引

架构流程

用户访问源域名
  ↓
Route53 解析到 CloudFront
  ↓
CloudFront Function 拦截请求(viewer-request)
  ↓
返回 301 重定向到目标域名

实施步骤

1. 创建 CloudFront Function

文件: redirect-function.js

function handler(event) {
    return {
        statusCode: 301,
        statusDescription: 'Moved Permanently',
        headers: {
            'location': { value: 'https://<TARGET_DOMAIN>' }  // 替换为目标域名
        }
    };
}

创建并发布:

# 创建 Function
aws cloudfront create-function \
  --name <FUNCTION_NAME> \
  --function-config '{"Comment": "Redirect <SOURCE_DOMAIN> to <TARGET_DOMAIN>", "Runtime": "cloudfront-js-2.0"}' \
  --function-code fileb://redirect-function.js \
  --region us-east-1 \
  --profile <AWS_PROFILE>

# 发布 Function(使用返回的 ETag)
aws cloudfront publish-function \
  --name <FUNCTION_NAME> \
  --if-match <ETAG_FROM_CREATE> \
  --region us-east-1 \
  --profile <AWS_PROFILE>

2. 申请 SSL 证书

申请证书:

aws acm request-certificate \
  --domain-name "<SOURCE_DOMAIN>" \
  --validation-method DNS \
  --subject-alternative-names "*.<SOURCE_DOMAIN>" \
  --region us-east-1 \
  --profile <AWS_PROFILE>

注意事项:

  • CloudFront 的 ACM 证书必须在 us-east-1 区域
  • 根域名重定向:申请 example.com + *.example.com
  • 子域名重定向:申请 *.example.com 或单独的子域名证书

DNS 验证:

# 获取验证记录
aws acm describe-certificate \
  --certificate-arn <CERTIFICATE_ARN> \
  --region us-east-1 \
  --profile <AWS_PROFILE> \
  --query 'Certificate.DomainValidationOptions[0].ResourceRecord'

# 添加 CNAME 记录到 Route53
aws route53 change-resource-record-sets \
  --hosted-zone-id <HOSTED_ZONE_ID> \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "<VALIDATION_NAME>",
        "Type": "CNAME",
        "TTL": 300,
        "ResourceRecords": [{"Value": "<VALIDATION_VALUE>"}]
      }
    }]
  }' \
  --profile <AWS_PROFILE>

3. 创建 CloudFront 分配

配置文件: cloudfront-config.json

{
  "CallerReference": "<UNIQUE_REFERENCE>",
  "Aliases": {
    "Quantity": 1,
    "Items": ["<SOURCE_DOMAIN>"]
  },
  "Origins": {
    "Quantity": 1,
    "Items": [{
      "Id": "dummy-origin",
      "DomainName": "example.com",
      "CustomOriginConfig": {
        "HTTPPort": 80,
        "HTTPSPort": 443,
        "OriginProtocolPolicy": "https-only"
      }
    }]
  },
  "DefaultCacheBehavior": {
    "TargetOriginId": "dummy-origin",
    "ViewerProtocolPolicy": "allow-all",
    "AllowedMethods": {
      "Quantity": 2,
      "Items": ["GET", "HEAD"],
      "CachedMethods": {
        "Quantity": 2,
        "Items": ["GET", "HEAD"]
      }
    },
    "Compress": true,
    "ForwardedValues": {
      "QueryString": false,
      "Cookies": {"Forward": "none"}
    },
    "MinTTL": 0,
    "FunctionAssociations": {
      "Quantity": 1,
      "Items": [{
        "FunctionARN": "<FUNCTION_ARN>",
        "EventType": "viewer-request"
      }]
    }
  },
  "Comment": "Redirect <SOURCE_DOMAIN> to <TARGET_DOMAIN>",
  "Enabled": true,
  "ViewerCertificate": {
    "ACMCertificateArn": "<CERTIFICATE_ARN>",
    "SSLSupportMethod": "sni-only",
    "MinimumProtocolVersion": "TLSv1.2_2021"
  }
}

创建分配:

aws cloudfront create-distribution \
  --distribution-config file://cloudfront-config.json \
  --region us-east-1 \
  --profile <AWS_PROFILE>

4. 配置 Route53 记录

aws route53 change-resource-record-sets \
  --hosted-zone-id <HOSTED_ZONE_ID> \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "<SOURCE_DOMAIN>",
        "Type": "A",
        "AliasTarget": {
          "HostedZoneId": "Z2FDTNDATAQYW2",
          "DNSName": "<CLOUDFRONT_DOMAIN>",
          "EvaluateTargetHealth": false
        }
      }
    }]
  }' \
  --region us-east-1 \
  --profile <AWS_PROFILE>

注意: Z2FDTNDATAQYW2 是 CloudFront 的固定托管区域 ID(全球通用)


验证测试

检查 CloudFront 部署状态

aws cloudfront get-distribution \
  --id <DISTRIBUTION_ID> \
  --profile <AWS_PROFILE> \
  --region us-east-1 \
  --query 'Distribution.Status' \
  --output text

测试重定向

# 测试 HTTP 请求
curl -I http://<SOURCE_DOMAIN>

# 测试 HTTPS 请求
curl -I https://<SOURCE_DOMAIN>

# 预期输出
HTTP/1.1 301 Moved Permanently
Location: https://<TARGET_DOMAIN>
X-Cache: FunctionGeneratedResponse from cloudfront

常见应用场景

场景 1: 根域名重定向到 www(推荐)

// example.com → www.example.com
function handler(event) {
    return {
        statusCode: 301,
        statusDescription: 'Moved Permanently',
        headers: {
            'location': { value: 'https://www.example.com' }
        }
    };
}

配置要点:

  • 证书:example.com + *.example.com
  • CloudFront Alias:example.com
  • Route53 A 记录:example.com → CloudFront

场景 2: 旧域名迁移到新域名

// old-site.com → new-site.com
function handler(event) {
    return {
        statusCode: 301,
        statusDescription: 'Moved Permanently',
        headers: {
            'location': { value: 'https://new-site.com' }
        }
    };
}

场景 3: 保留路径的重定向

// old.example.com/path → new.example.com/path
function handler(event) {
    var request = event.request;
    var uri = request.uri;
    var querystring = request.querystring;
    
    var newUrl = 'https://new.example.com' + uri;
    if (Object.keys(querystring).length > 0) {
        newUrl += '?' + new URLSearchParams(querystring).toString();
    }
    
    return {
        statusCode: 301,
        statusDescription: 'Moved Permanently',
        headers: {
            'location': { value: newUrl }
        }
    };
}

成本估算

基于 AWS 官方定价(2025年):

费用构成

项目 费用 说明
CloudFront Functions 调用 $0.10/百万次 每次重定向触发一次调用
CloudFront HTTPS 请求 $1.00/百万次 美国区域定价
CloudFront HTTP 请求 $0.75/百万次 如果只用 HTTP
Route53 查询 $0.40/百万次 DNS 解析费用
CloudFront 数据传输 ~$0.00 301 响应几乎无数据传输
ACM 证书 免费 包含在服务中

免费额度(Always Free Tier)

  • CloudFront Functions: 前 2,000,000 次调用/月
  • CloudFront 请求: 前 10,000,000 次 HTTP/HTTPS 请求/月
  • CloudFront 数据传输: 前 1 TB/月
  • ACM 证书: 完全免费

实际成本示例

小流量场景(< 200万次/月):

  • 完全免费(在免费额度内)

中等流量(100万次 HTTPS 重定向/月,超出免费额度):

  • CloudFront Functions: $0.10
  • CloudFront HTTPS 请求: $1.00
  • Route53 查询: $0.40
  • 总计: $1.50/百万次

大流量(1000万次 HTTPS 重定向/月):

  • CloudFront Functions: $1.00(10M × $0.10)
  • CloudFront HTTPS 请求: $10.00(10M × $1.00)
  • Route53 查询: $4.00(10M × $0.40)
  • 总计: $15.00/月

参考: CloudFront 官方定价


故障排查

问题 1: 证书验证失败

  • 检查 Route53 中是否存在验证 CNAME 记录
  • 等待 DNS 传播(最多 30 分钟)
  • 确认证书在 us-east-1 区域

问题 2: CloudFront 返回 502/503

  • 检查 Function 是否已发布(Status: LIVE)
  • 检查 Function 是否正确关联到 viewer-request
  • 验证 Function 代码语法正确

问题 3: 重定向不生效

  • 确认 CloudFront 状态为 Deployed(需要 5-15 分钟)
  • 清除浏览器缓存
  • 使用 curl 测试避免浏览器缓存
  • 检查 DNS 是否已传播:dig <SOURCE_DOMAIN>

问题 4: HTTPS 证书错误

  • 确认 CloudFront Alias 与证书域名匹配
  • 检查证书是否包含源域名(通配符或精确匹配)
  • 验证证书状态为 ISSUED
Previous Post
No Comment
Add Comment
comment url