首页 > 解决方案 > 如何为两种 CloudFront 行为使用不同的错误配置?

问题描述

我有一个 CloudFront 分配,其自定义源指向所有其他路径上的 ALB/api/*和一个 S3 源,用于所有其他路径上的静态站点(分配的默认行为)。这为存储在 S3 中的 Vue 应用程序和在 Fargate 中运行的由 ALB 负载平衡的 API 提供服务。

在 CloudFront 分配的错误配置中,我有一个发送 404 响应的规则/index.html(这是 SPA 所必需的,以便 JavaScript 路由器可以拾取嵌套路由)。目前,当 API 返回 404 响应时,它给出的是/index.html响应,而不是来自 API 的 404 响应。这是有道理的,但这不是我想要的行为。有没有办法获得 API 404 响应/api/*,但/index.html仅用作默认 S3 Origin/默认行为的 404 响应?

我最初在子域上提供了 ALB,所以这不是问题。我试图通过在同一个域 ( app.mydomain.com) 上提供 API 和前端站点来简化事情,而不是在 上提供前端mydomain.com和 API api.mydomain.com,但这会产生一个带有错误响应的新错误。

我正在使用 CDK 构建此应用程序,这是我的 CloudFront 分配的代码,它导致 404 响应问题:

        path_patterns = ["/api/*", "/admin/*"]
        self.distribution = cloudfront.CloudFrontWebDistribution(
            self,
            "CloudFrontDistribution",
            origin_configs=[
                cloudfront.SourceConfiguration(
                    custom_origin_source=cloudfront.CustomOriginConfig(
                        domain_name=alb,
                        origin_protocol_policy=cloudfront.OriginProtocolPolicy.MATCH_VIEWER,
                    ),
                    behaviors=[
                        cloudfront.Behavior(
                            allowed_methods=cloudfront.CloudFrontAllowedMethods.ALL,
                            path_pattern=path_pattern,
                            forwarded_values={
                                "headers": ["*"],
                                "cookies": {"forward": "all"},
                                "query_string": True,
                            },
                        )
                        for path_pattern in path_patterns
                    ],
                ),
                cloudfront.SourceConfiguration(
                    s3_origin_source=cloudfront.S3OriginConfig(
                        s3_bucket_source=self.static_site_bucket
                    ),
                    behaviors=[
                        cloudfront.Behavior(
                            is_default_behavior=True,
                            cached_methods=cloudfront.CloudFrontAllowedMethods.GET_HEAD,
                        )
                    ],
                ),
            ],
            alias_configuration=cloudfront.AliasConfiguration(
                acm_cert_ref=certificate.certificate_arn,
                names=[full_domain_name],
            ),
            error_configurations=[
                {
                    "errorCode": 403,
                    "errorCachingMinTtl": 0,
                    "responseCode": 200,
                    "responsePagePath": "/index.html",
                },
                {
                    "errorCode": 404,
                    "errorCachingMinTtl": 0,
                    "responseCode": 200,
                    "responsePagePath": "/index.html",
                },
            ],
        )

编辑:2020-05-13

我已经尝试从 S3 存储桶中删除error_configuration并且还CloudFrontWebDistribution设置了website_index_document和:website_error_document

        self.static_site_bucket = s3.Bucket(
            self,
            "StaticSiteBucket",
            access_control=s3.BucketAccessControl.PUBLIC_READ,
            bucket_name=f"{full_app_name}-frontend",
            removal_policy=core.RemovalPolicy.DESTROY,
            website_index_document="index.html",
            website_error_document="index.html",
        )

通过此更改,当我希望看到我的 Vue 应用程序提供的 404 页面时,我仍然收到 Key Error 404 响应。

在我的 CloudFront 配置中为静态站点使用 S3 源是否正确,还是应该是指向存储桶网站 URL 的自定义源?

我没有为 ALB 和 S3 网站使用自定义来源。以下是 CDK 中描述的所有资源:

from aws_cdk import (
    aws_certificatemanager as acm,
    aws_s3 as s3,
    aws_cloudfront as cloudfront,
    aws_route53 as route53,
    aws_iam as iam,
    aws_route53_targets as targets,
    core,
)


class CloudFront(core.Construct):
    def __init__(
        self,
        scope: core.Construct,
        id: str,
        hosted_zone: route53.IHostedZone,
        certificate: acm.ICertificate,
        alb: str,
        full_domain_name: str,
        full_app_name: str,
        **kwargs,
    ) -> None:
        super().__init__(scope, id, **kwargs)

        self.static_site_bucket = s3.Bucket(
            self,
            "StaticSiteBucket",
            access_control=s3.BucketAccessControl.PUBLIC_READ,
            bucket_name=f"{full_app_name}-frontend",
            removal_policy=core.RemovalPolicy.DESTROY,
            website_index_document="index.html",
            website_error_document="index.html",
        )

        self.policy_statement = iam.PolicyStatement(
            actions=["s3:GetObject"],
            resources=[f"{self.static_site_bucket.bucket_arn}/*"],
        )

        self.policy_statement.add_any_principal()

        self.static_site_policy_document = iam.PolicyDocument(
            statements=[self.policy_statement]
        )

        self.static_site_bucket.add_to_resource_policy(self.policy_statement)

        path_patterns = ["/api/*", "/admin/*", "/flower/*"]
        self.distribution = cloudfront.CloudFrontWebDistribution(
            self,
            "CloudFrontDistribution",
            origin_configs=[
                cloudfront.SourceConfiguration(
                    custom_origin_source=cloudfront.CustomOriginConfig(
                        domain_name=alb,
                        origin_protocol_policy=cloudfront.OriginProtocolPolicy.MATCH_VIEWER,  # noqa
                    ),
                    behaviors=[
                        cloudfront.Behavior(
                            allowed_methods=cloudfront.CloudFrontAllowedMethods.ALL,  # noqa
                            path_pattern=path_pattern,
                            forwarded_values={
                                "headers": ["*"],
                                "cookies": {"forward": "all"},
                                "query_string": True,
                            },
                        )
                        for path_pattern in path_patterns
                    ],
                ),
                cloudfront.SourceConfiguration(
                    custom_origin_source=cloudfront.CustomOriginConfig(
                        domain_name=self.static_site_bucket.bucket_domain_name,
                        origin_protocol_policy=cloudfront.OriginProtocolPolicy.MATCH_VIEWER,  # noqa
                    ),
                    behaviors=[
                        cloudfront.Behavior(
                            is_default_behavior=True,
                            cached_methods=cloudfront.CloudFrontAllowedMethods.GET_HEAD,  # noqa
                        )
                    ],
                ),
            ],
            alias_configuration=cloudfront.AliasConfiguration(
                acm_cert_ref=certificate.certificate_arn,
                names=[full_domain_name],
            ),
        )

        route53.ARecord(
            self,
            "AliasRecord",
            target=route53.AddressRecordTarget.from_alias(
                targets.CloudFrontTarget(self.distribution)
            ),
            zone=hosted_zone.hosted_zone,
            record_name=f"{full_domain_name}.",
        )

编辑 2020-05-14

我为自定义来源使用了错误的域名。我试过了bucket_regional_domain_namebucket_website_domain_name但这些都给了我my-s3-bucket-name.s3.us-east-1.amazonaws.com。这是我用于自定义源配置的设置:

                cloudfront.SourceConfiguration(
                    custom_origin_source=cloudfront.CustomOriginConfig(
                        domain_name=f"{self.static_site_bucket.bucket_name}.s3-website-us-east-1.amazonaws.com",
                        origin_protocol_policy=cloudfront.OriginProtocolPolicy.HTTP_ONLY,
                    ),
                    behaviors=[
                        cloudfront.Behavior(
                            is_default_behavior=True,
                            cached_methods=cloudfront.CloudFrontAllowedMethods.GET_HEAD,
                        )
                    ],
                ),

HTTP_ONLY很重要,这不适用于MATCH_VIEWER. 另外,请注意我如何使用 f 字符串来获取s3-website存储桶的正确 URL。我仍然不确定这是否可以在 CDK 中自动找到。如果我发现更多,我会更新这个。

标签: amazon-web-servicesamazon-s3amazon-cloudfrontaws-cdk

解决方案


@briancaffey,很高兴再次为您提供帮助。在 CloudFront 中无法做到这一点。或者,我认为以下方法应该有效。

  1. 在 CloudFront 中删除自定义错误响应
  2. 在您的 S3 源中配置自定义错误文档。[参考]
  3. 将CloudFront 更改S3OriginCustomOrigin将 S3 网站终端节点设置为源域名。

推荐阅读