如何为两种 CloudFront 行为使用不同的错误配置?

How can I use different error configurations for two CloudFront behaviors?

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

在 CloudFront 分配的错误配置中,我有一条规则向 /index.html 发送 404 响应(这是 SPA 所必需的,以便 javascript 路由器可以拾取嵌套路由)。目前,当 API returns 一个 404 响应时,它给出 /index.html 响应,而不是 API 的 404 响应。这是有道理的,但这不是我想要的行为。有没有办法在 /api/* 上获得 API 404 响应,但仅针对默认 S3 Origin/default 行为使用 /index.html 作为 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

我已尝试从 CloudFrontWebDistribution 中删除 error_configuration 并且还在 S3 存储桶上设置了 website_index_documentwebsite_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 页面时,我仍然收到关键错误 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 中自动找到的东西。如果我发现更多,我会更新这个。

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

  1. 删除 CloudFront 中的自定义错误响应
  2. 在您的 S3 源中配置自定义错误文档。 [ref]
  3. 将 CloudFront S3Origin 更改为 CustomOrigin,并将 S3 网站端点设置为源域名。