Hugo部署S3+CloudFront无法自动跳转index.html的Terraform配置问题
问题解决:Hugo子路径访问403的Terraform配置修改
核心问题分析
你的配置中default_root_object = "index.html"仅对根路径/生效,访问子路径如/blog/post-1时,CloudFront会直接向S3请求blog/post-1对象,但Hugo生成的静态文件实际是blog/post-1/index.html,导致S3返回访问拒绝。同时viewer_certificate配置存在冲突(同时启用默认证书和自定义ACM证书),需要同步修正。
修改后的完整配置
1. 添加Lambda@Edge路径重写函数
创建Origin Request类型的Lambda@Edge函数,自动为无后缀的请求路径追加/index.html(Lambda@Edge必须部署在us-east-1区域):
# 新增us-east-1区域的AWS Provider provider "aws" { alias = "us_east_1" region = "us-east-1" } # Lambda函数角色(Edge函数需额外信任edgelambda服务) resource "aws_iam_role" "lambda_edge_role" { provider = aws.us_east_1 name = "lambda-edge-hugo-path-rewrite-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = [ "lambda.amazonaws.com", "edgelambda.amazonaws.com" ] } } ] }) } # 附加基础执行权限 resource "aws_iam_role_policy_attachment" "lambda_edge_basic_execution" { provider = aws.us_east_1 role = aws_iam_role.lambda_edge_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" } # Lambda@Edge函数本体 resource "aws_lambda_function" "cloudfront_path_rewrite" { provider = aws.us_east_1 filename = "lambda_function_payload.zip" function_name = "cloudfront-hugo-path-rewrite" role = aws_iam_role.lambda_edge_role.arn handler = "index.lambda_handler" runtime = "nodejs20.x" source_code_hash = filebase64sha256("lambda_function_payload.zip") }
Lambda函数代码(需打包为lambda_function_payload.zip)
创建index.js文件,写入以下逻辑:
exports.lambda_handler = async (event) => { const request = event.Records[0].cf.request; const uri = request.uri; // 对无文件后缀的路径追加/index.html if (!uri.match(/\.\w+$/) && !uri.endsWith('/')) { request.uri = `${uri}/index.html`; } else if (uri.endsWith('/')) { request.uri = `${uri}index.html`; } return request; };
2. 修正CloudFront分发配置
更新aws_cloudfront_distribution资源,添加Lambda@Edge触发器,并修复证书配置冲突:
resource "aws_cloudfront_distribution" "my_cloudfront" { depends_on = [ aws_s3_bucket.my_site_bucket, aws_lambda_function.cloudfront_path_rewrite ] origin { domain_name = aws_s3_bucket.my_site_bucket.bucket_regional_domain_name origin_id = "s3-cloudfront" s3_origin_config { origin_access_identity = aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path } } enabled = true is_ipv6_enabled = true default_root_object = "index.html" aliases = [var.domain_name] restrictions { geo_restriction { restriction_type = "none" } } default_cache_behavior { allowed_methods = ["GET", "HEAD"] cached_methods = ["GET", "HEAD"] target_origin_id = "s3-cloudfront" forwarded_values { query_string = false cookies { forward = "none" } } viewer_protocol_policy = "redirect-to-https" min_ttl = 0 default_ttl = 3600 max_ttl = 86400 # 关联Lambda@Edge路径重写触发器 lambda_function_association { event_type = "origin-request" lambda_arn = "${aws_lambda_function.cloudfront_path_rewrite.arn}:${aws_lambda_function.cloudfront_path_rewrite.version}" include_body = false } } price_class = "PriceClass_200" viewer_certificate { # 禁用默认证书,使用自定义ACM证书 cloudfront_default_certificate = false acm_certificate_arn = aws_acm_certificate.my_site.arn ssl_support_method = "sni-only" minimum_protocol_version = "TLSv1.2_2021" # 推荐使用更安全的协议版本 } tags = local.cdn_tags }
3. 可选:添加兜底自定义错误响应
如果Lambda覆盖场景存在遗漏,可添加自定义错误响应,将403/404错误映射到index.html:
resource "aws_cloudfront_distribution" "my_cloudfront" { # ... 其他配置 ... custom_error_response { error_code = 403 response_code = 200 response_page_path = "/index.html" } custom_error_response { error_code = 404 response_code = 200 response_page_path = "/index.html" } }
部署步骤
- 将上述Lambda代码打包为
lambda_function_payload.zip - 执行
terraform init、terraform plan、terraform apply部署配置 - 等待CloudFront分发更新完成(通常需10-15分钟)
内容的提问来源于stack exchange,提问作者JPFrancoia
相关产品推荐
相关产品推荐

