如何解决boto3 put_bucket_website方法中的CORS配置错误问题?

问题现象描述

当开发者使用boto3.client('s3').put_bucket_website()方法配置S3静态网站托管时,经常会遇到跨域资源共享(CORS)相关的403错误。典型错误场景包括:

  • 前端JavaScript无法加载S3存储桶中的字体文件(.woff, .ttf)
  • AJAX请求返回No 'Access-Control-Allow-Origin' header错误
  • 控制台出现Preflight response didn't pass access control check警告

根本原因分析

该问题的核心在于S3存储桶的CORS策略网站配置未正确协同工作:

  1. 默认CORS限制:新创建的S3存储桶默认拒绝所有跨域请求
  2. 配置缺失put_bucket_website只设置网站属性,不自动配置CORS
  3. 预检请求(Preflight):浏览器对非简单请求会先发送OPTIONS请求验证CORS

解决方案

方案1:通过put_bucket_cors同步配置

import boto3

s3 = boto3.client('s3')
bucket_name = 'your-bucket-name'

s3.put_bucket_website(
    Bucket=bucket_name,
    WebsiteConfiguration={
        'IndexDocument': {'Suffix': 'index.html'},
        'ErrorDocument': {'Key': 'error.html'}
    }
)

s3.put_bucket_cors(
    Bucket=bucket_name,
    CORSConfiguration={
        'CORSRules': [
            {
                'AllowedHeaders': ['*'],
                'AllowedMethods': ['GET', 'HEAD'],
                'AllowedOrigins': ['*'],
                'ExposeHeaders': [],
                'MaxAgeSeconds': 3000
            }
        ]
    }
)

方案2:使用CloudFront分发

通过CloudFront的缓存行为设置可覆盖CORS头:

  • 添加Access-Control-Allow-Origin响应头
  • 设置Origin为白名单或通配符
  • 配置OPTIONS请求缓存策略

方案3:S3预签名URL

对于敏感资源,可采用临时访问凭证方案:

url = s3.generate_presigned_url(
    'get_object',
    Params={'Bucket': bucket_name, 'Key': object_key},
    ExpiresIn=3600
)

最佳实践建议

场景 推荐方案 安全性
公开静态资源 CORS通配符+网站托管 ★☆☆☆☆
企业内网应用 指定具体域名+CloudFront ★★★☆☆
敏感数据访问 预签名URL+VPC端点 ★★★★★

调试技巧

使用Chrome开发者工具进行问题诊断:

  1. 检查Network面板中的请求头
  2. 验证Response Headers是否包含CORS相关头
  3. 使用curl -v -X OPTIONS http://bucket.s3-website-region.amazonaws.com测试预检请求