将 CloudFront 配置为代理查询字符串作为 S3 对象键的一部分?

Configure CloudFront to proxy query string as part of S3 object key?

我们将 AWS CloudFront 配置为将请求代理到 S3 存储桶。

AWS CloudFront 允许您对其进行配置,以便查询字符串参数也被代理到原始服务器 (docs) - 我们也进行了此配置。

我遇到的问题是查询字符串参数似乎没有包含在 S3 对象键查找中。

例如,我们有像这样的对象键:

但是如果我们发出像 https://cf-distro-url.com/foo?a=1 or https://cf-distro-url.com/foo?b=2 这样的请求,我们实际上会得到 /foo 内容而不是特定的关键内容。

看起来 CloudFront 可能正在代理查询字符串参数,但它们并未用作 S3 对象键查找过程的一部分。

有没有人遇到过这种情况? and/or知道解决方案吗?

谢谢!

使用 Web 浏览器,如果直接访问 CloudFront 和 S3,都不会像您期望的那样在对象键中使用 ?,因为未转义的 ? 是分隔符对于 HTTP 中的查询字符串。

根据定义,? 标志着路径的结束,因此也是对象键的结束。

获取密钥中带有 ? 的对象的唯一方法是 浏览器 发送带有 ? 转义为 %3F,这意味着 URL 最初必须以这种方式呈现给浏览器。

您不能将键名称中带有 ? 的对象上传到 S3,然后使用 URL 中的文字 ? 从浏览器访问它。

使用 curl 进行了演示,但浏览器行为相同。

在 URI 中使用 /foo?bar

$ curl -v 'http://.....s3.amazonaws.com/foo?bar=1'
* About to connect() to .....s3.amazonaws.com port 80 (#0)
*   Trying x.x.x.x... connected
> GET /foo?bar=1 HTTP/1.1

< HTTP/1.1 404 Not Found

<?xml version="1.0" encoding="UTF-8"?>
<Error>
  <Code>NoSuchKey</Code>
  <Message>The specified key does not exist.</Message>
  <Key>foo</Key>                                      <<< requested object is "foo"
  <RequestId>...</RequestId>
  <HostId>...</HostId>
</Error>

在 URI 中使用 /foo%3Fbar=1

$ curl -v 'http://.....s3.amazonaws.com/foo%3Fbar=1'
* About to connect() to .....s3.amazonaws.com port 80 (#0)
*   Trying x.x.x.x... connected
> GET /foo%3Fbar=1 HTTP/1.1

< HTTP/1.1 404 Not Found

<?xml version="1.0" encoding="UTF-8"?>
<Error>
  <Code>NoSuchKey</Code>
  <Message>The specified key does not exist.</Message>
  <Key>foo?bar=1</Key>                                <<< requested object is "foo?bar=1"
  <RequestId>...</RequestId>
  <HostId>...</HostId>
</Error>

这是 URL 形成方式的限制,而不是在 S3(或 CloudFront)中...但在 S3 文档中提到:

The following characters in a key name may require additional code handling and will likely need to be URL encoded or referenced as HEX

...

Question mark ("?")

http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html#object-keys

如果您能够使用 SDK 正确上传此类对象,那么它正在为您转义字符。 Web 浏览器不会隐式地这样做,因为 ? 具有特定的含义。

无论 CloudFront 是否将查询字符串传递给 S3,网络行为都是相同的——查询字符串不是键的一部分。

看起来您发送的方式是由 CloudFront 翻译的。您需要像下面这样查询对象

https://cf-distro-url.com/foo%3Fa%3D1

您需要对这些字符执行 URI 编码,以便将它们作为对象名称传递给 S3。

希望对您有所帮助。