У меня есть веб-страница ( https://smartystreets.com/contact ), которая использует jQuery для загрузки некоторых файлов SVG из S3 через CloudFront CDN.
В Chrome я открою окно Incognito, а также консоль. Тогда я буду загружать страницу. По мере загрузки страницы я обычно получаю от 6 до 8 сообщений в консоли, которые выглядят примерно так:
XMLHttpRequest cannot load
https://d79i1fxsrar4t.cloudfront.net/assets/img/feature-icons/documentation.08e71af6.svg.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'https://smartystreets.com' is therefore not allowed access.
Если я делаю стандартную перезагрузку страницы, даже несколько раз, я продолжаю получать те же ошибки. Если я это сделаю, Command+Shift+R
то большинство, а иногда и все изображения будут загружаться без XMLHttpRequest
ошибок.
Иногда даже после загрузки изображений я обновляюсь, и одно или несколько изображений не загружаются и возвращают эту XMLHttpRequest
ошибку снова.
Я проверил, изменил и перепроверил настройки на S3 и Cloudfront. В S3 моя конфигурация CORS выглядит так:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedOrigin>http://*</AllowedOrigin>
<AllowedOrigin>https://*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
</CORSConfiguration>
(Примечание: изначально было только <AllowedOrigin>*</AllowedOrigin>
, та же проблема.)
В CloudFront поведение распределения устанавливается , чтобы позволить методы HTTP: GET, HEAD, OPTIONS
. Кэшированные методы одинаковы. Прямые заголовки установлены в «Белый список», и этот белый список включает в себя «Access-Control-Request-Headers, Access-Control-Request-Method, Origin».
Тот факт, что он работает после перезагрузки браузера без кеша, указывает на то, что все хорошо на стороне S3 / CloudFront, иначе зачем доставлять контент. Но тогда почему контент не будет доставлен при начальном просмотре страницы?
Я работаю в Google Chrome на MacOS. Firefox без проблем получает файлы каждый раз. Опера НИКОГДА не получает файлы. Safari подберет изображения после нескольких обновлений.
Использование curl
у меня не вызывает никаких проблем:
curl -I -H 'Origin: smartystreets.com' https://d79i1fxsrar4t.cloudfront.net/assets/img/phone-icon-outline.dc7e4079.svg
HTTP/1.1 200 OK
Content-Type: image/svg+xml
Content-Length: 508
Connection: keep-alive
Date: Tue, 20 Jun 2017 17:35:57 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000
Last-Modified: Thu, 15 Jun 2017 16:02:19 GMT
ETag: "dc7e4079f937e83291f2174853adb564"
Cache-Control: max-age=31536000
Expires: Wed, 01 Jan 2020 23:59:59 GMT
Accept-Ranges: bytes
Server: AmazonS3
Vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
Age: 4373
X-Cache: Hit from cloudfront
Via: 1.1 09fc52f58485a5da8e63d1ea27596895.cloudfront.net (CloudFront)
X-Amz-Cf-Id: wxn_m9meR6yPoyyvj1R7x83pBDPJy1nT7kdMv1aMwXVtHCunT9OC9g==
Некоторые предлагают мне удалить дистрибутив CloudFront и воссоздать его. Похоже, довольно резкое и неудобное решение.
Что вызывает эту проблему?
Обновить:
Добавление заголовков ответов из изображения, которое не удалось загрузить.
age:1709
cache-control:max-age=31536000
content-encoding:gzip
content-type:image/svg+xml
date:Tue, 20 Jun 2017 17:27:17 GMT
expires:2020-01-01T23:59:59.999Z
last-modified:Tue, 11 Apr 2017 18:17:41 GMT
server:AmazonS3
status:200
vary:Accept-Encoding
via:1.1 022c901b294fedd7074704d46fce9819.cloudfront.net (CloudFront)
x-amz-cf-id:i0PfeopzJdwhPAKoHpbCTUj1JOMXv4TaBgo7wrQ3TW9Kq_4Bx0k_pQ==
x-cache:Hit from cloudfront
Ответы:
Вы делаете два запроса на один и тот же объект, один из HTML, другой из XHR. Второй сбой, потому что Chrome использует кэшированный ответ от первого запроса, который не имеет
Access-Control-Allow-Origin
заголовка ответа.Зачем?
Ошибка Chromium 409090 Не удается выполнить перекрестный запрос из кэша после кэширования обычного запроса , и эта проблема «не будет устранена» - они считают, что их поведение правильное. Chrome считает, что кэшированный ответ можно использовать, очевидно, потому что в ответ не был включен
Vary: Origin
заголовок.Но S3 не возвращается,
Vary: Origin
когда объект запрашивается безOrigin:
заголовка запроса, даже когда CORS настроен в корзине.Vary: Origin
отправляется только тогда, когдаOrigin
в запросе присутствует заголовок.А CloudFront не добавляется
Vary: Origin
дажеOrigin
в белый список для пересылки, что по определению должно означать, что изменение заголовка может изменить ответ - вот почему вы пересылаете и кэшируете по заголовкам запросов.CloudFront получает пропуск, потому что его ответ был бы правильным, если бы S3 был более правильным, поскольку CloudFront действительно возвращает его, когда он предоставляется S3.
S3, немного пушистый. Это не так, чтобы вернуться,
Vary: Some-Header
когда не былоSome-Header
в запросе.Понятно, что
Vary: Some-Absent-Header
он действителен, поэтому S3 будет корректным, если он добавитVary: Origin
к своему ответу, если настроен CORS, поскольку это действительно может изменить ответ.И, видимо, это заставило бы Chrome делать правильные вещи. Или, если в этом случае это не так, это будет нарушением
MUST NOT
. Из того же раздела:Таким образом, S3 действительно
SHOULD
возвращается,Vary: Origin
когда CORS настроен в сегменте, еслиOrigin
отсутствует в запросе, но это не так.Тем не менее, S3 не является строго неправильным для того, чтобы не возвращать заголовок, потому что это только a
SHOULD
, а не aMUST
. Опять же из того же раздела RFC-7231:С другой стороны, можно утверждать, что Chrome должен неявно знать, что изменение
Origin
заголовка должно быть ключом кеша, поскольку он может изменить ответ таким же образом, чтоAuthorization
может изменить ответ.Точно так же повторное использование в разных источниках возможно ограничено природой,
Origin
но этот аргумент не является сильным.tl; dr: Вы, очевидно, не можете успешно извлечь объект из HTML, а затем успешно извлечь его снова, как запрос CORS с Chrome и S3 (с или без CloudFront), из-за особенностей реализации.
Временное решение:
Это поведение можно обойти с помощью CloudFront и Lambda @ Edge, используя следующий код в качестве триггера Origin Response.
Это добавляет
Vary: Access-Control-Request-Headers, Access-Control-Request-Method, Origin
к любому ответу от S3, который не имеетVary
заголовка. В противном случаеVary
заголовок в ответе не изменяется.Атрибуция. Я также являюсь автором исходного поста на форумах поддержки AWS, где этот код был опубликован изначально.
Решение Lambda @ Edge, приведенное выше, приводит к полностью корректному поведению, но вот две альтернативы, которые вы можете найти полезными, в зависимости от ваших конкретных потребностей:
Альтернатива / Hackaround # 1: Подделать заголовки CORS в CloudFront.
CloudFront поддерживает пользовательские заголовки, которые добавляются к каждому запросу. Если вы установите
Origin:
для каждого запроса, даже те, которые не являются кросс-источником, это включит правильное поведение в S3. Параметр конфигурации называется Custom Origin Headers, а слово «Origin» означает нечто совершенно иное, чем в CORS. Конфигурирование пользовательского заголовка, подобного этому, в CloudFront перезаписывает то, что отправлено в запросе, с указанным значением, или добавляет его, если оно отсутствует. Если у вас есть только один источник доступа к вашему контенту через XHR, напримерhttps://example.com
, вы можете добавить это. Использование*
сомнительно, но может работать для других сценариев. Тщательно обдумайте последствия.Альтернатива / Hackaround # 2: Используйте «пустой» параметр строки запроса, который отличается для HTML и XHR или отсутствует у одного или другого. Эти параметры обычно называются,
x-*
но не должны бытьx-amz-*
.Допустим, вы придумали имя
x-request
. Так<img src="https://dzczcexample.cloudfront.net/image.png?x-request=html">
. При доступе к объекту из JS не добавляйте параметр запроса. CloudFront уже делает правильные вещи, кэшируя разные версии объектов, используяOrigin
заголовок или его отсутствие как часть ключа кэша, потому что вы перенаправили этот заголовок в свое поведение кэша. Проблема в том, что ваш браузер этого не знает. Это убеждает браузер в том, что на самом деле это отдельный объект, который необходимо запросить снова, в контексте CORS.Если вы используете эти альтернативные предложения, используйте одно или другое, а не оба.
источник
?x-some-key=some-value
параметра строки запроса убедит браузер, что запрос отличается.Я не знаю, почему вы получаете такие разные результаты из разных браузеров, но:
Эта строка прямо здесь - это то, что (если вы можете привлечь их внимание) инженер CloudFront или Служба поддержки будет использовать для выполнения одного из ваших неудавшихся запросов. Если запрос поступает на сервер CloudFront, он должен иметь этот заголовок в ответе. Если этот заголовок отсутствует, то, скорее всего, запрос где-то завершится неудачей, прежде чем он попадет в CloudFront.
источник