通配子域指向适当的 S3/CloudFront 子目录

Wildcard subdomains point to appropriate S3/CloudFront subdirectories

我需要多个子域来指向 Amazon S3(同步到 CloudFront 分发版)上的个人 buckets/subdirectories,我在其中托管一些静态文件。

所以任何

SUBDOMAINNAME.example.com

自动指向

s3.amazonaws.com/somebucket/SUBDOMAINNAME

somedistributionname.cloudfront.net/SUBDOMAINNAME

有没有办法在没有 运行 重定向服务器的情况下完成此操作?

是否可以在不更改每个新子域的 DNS 记录的情况下完成,或者如果不能,以编程方式添加 DNS 规则?

就资源使用而言,最有效的方法是什么。 (可能有数百个子域,每个子域每天有数百个请求)

更新:这个答案在写的时候是正确的,下面描述的技术仍然完全可行,但可能不太理想,因为 Lambda@Edge 现在可以用来完成这个 objective,因为我在 Serving a multitude of static sites from a wildcard domain in AWS.

的回答中解释了

不,没有办法自动执行此操作。

Is there a way to accomplish this without running a server for redirection?

从技术上讲,您不需要重定向来完成此操作。您需要重写路径,这就是为什么您的最终问题的答案是 "no"——因为 Route 53(和一般的 DNS)不能做任何与路径相关的事情。

Route 53 确实支持通配符 DNS,但如果没有 CloudFront and/or S3 支持将主机 header 从 HTTP 请求放入路径(他们不支持)的机制,那帮助有限).

现在,这可以在 "zero-touch" 模式下轻松完成,其中包含单个 Route 53 * 通配符条目、为 *.example.com 配置的单个 CloudFront 分配以及一个或多个 EC2 instances 运行 HAProxy 重写请求路径并将请求转发到 S3 存储桶。基本配置文件中的一行将完成该请求重写:

http-request set-path /%[req.hdr(host)]%[path] 

然后您需要代理将实际的存储桶端点主机名发送到 S3,而不是浏览器提供的主机名:

http-request set-header Host example-bucket.s3.amazonaws.com

代理会将修改后的请求发送到 S3,return S3 对 CloudFront 的响应,这将 return 对浏览器的响应。

但是,如果您不想采用这种方法,因为需要服务器,那么替代解决方案如下所示:

  • 为每个子域配置 CloudFront 分配,为分配设置 alternate domain name 以匹配特定的子域。

  • 将每个子域分布的 Origin 配置为指向同一个存储桶,将 origin path 设置为 /one-specific-subdomain.example.com。在将请求发送到 S3 之前,CloudFront 会将 GET /images/funny-cat.jpg HTTP/1.1 的请求更改为 GET /one-specific-subdomain.example.com/images/funny-cat.jpg HTTP/1.1,从而导致您描述的行为。 (这与我为 HAProxy 描述的行为的最终结果相同,但它是静态的,而不是动态的,因此每个子域一个分布;在这两种情况下都不会是 "redirect"——所以地址栏不会改变).

  • 在 Route 53 中为每个子域配置一个 A-record 别名,指向子域的特定 CloudFront 分配。

这一切都可以通过 API、使用任何一个 SDK 或使用 aws-cli 以编程方式完成,这是一种非常简单的测试方法,原型,并在不编写太多代码的情况下编写此类内容的脚本。 CloudFront 和 Route 53 都完全 automation-friendly.

请注意,使用自己的 CloudFront 分配的每个站点都没有明显的缺点,因为您的命中率不会有所不同,并且分配没有单独收费——只有请求和带宽费用。

另请注意,CloudFront 有一个默认值 limit of 200 distributions per AWS account,但这是一个软限制,可以通过向 AWS 支持部门发送请求来增加。

通配符适用于 S3。我只是放了一个指向 IP 的 A 记录 *,它起作用了。

自 Lambda@edge 以来,这可以通过 Cloud Front "Viewer Request" 事件触发的 lambda 函数来完成。

这是这样一个 Lambda 函数的示例,其中像 foo.example.com/index.html 这样的请求将 return 来自您来源的文件 /foo/index.html

您需要一个带有 CNAME *.example.com 的 CF 分发包,以及指向它的 A 记录“*.example.com”

exports.handler = (event, context, callback) => {
  const request = event.Records[0].cf.request;
  const subdomain = getSubdomain(request);
  if (subdomain) {
    request.uri = '/' + subdomain + request.uri;
  }
  callback(null, request);
};

function getSubdomain(request) {
  const hostItem = request.headers.host.find(item => item.key === 'Host');
  const reg = /(?:(.*?)\.)[^.]*\.[^.]*$/;
  const [_, subdomain] = hostItem.value.match(reg) || [];
  return subdomain;
}

至于费用请看lambda pricing。当前定价为每百万请求 0.913 美元