Mam stronę internetową ( https://smartystreets.com/contact ), która używa jQuery do ładowania niektórych plików SVG z S3 za pośrednictwem CDN CloudFront.
W Chrome otworzę okno Incognito oraz konsolę. Następnie załaduję stronę. Gdy strona się ładuje, zazwyczaj w konsoli pojawia się od 6 do 8 wiadomości, które wyglądają podobnie do tego:
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.
Jeśli wykonam standardowe ładowanie strony, nawet wielokrotnie, nadal pojawiają się te same błędy. Jeśli to zrobię, Command+Shift+R
większość, a czasem wszystkie obrazy zostaną załadowane bez XMLHttpRequest
błędu.
Czasami nawet po załadowaniu obrazów odświeżę się, a jeden lub więcej obrazów nie załaduje się i nie zwróci XMLHttpRequest
ponownie tego błędu.
Sprawdziłem, zmieniłem i ponownie sprawdziłem ustawienia S3 i Cloudfront. W S3 moja konfiguracja CORS wygląda następująco:
<?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>
(Uwaga: początkowo miał tylko ten <AllowedOrigin>*</AllowedOrigin>
sam problem).
W CloudFront zachowanie dystrybucja jest ustawiony, aby umożliwić metod HTTP: GET, HEAD, OPTIONS
. Metody buforowane są takie same. Przekazywanie nagłówków jest ustawione na „Biała lista”, a ta biała lista zawiera „Nagłówki kontroli dostępu, nagłówki żądania, metoda kontroli dostępu, pochodzenie”.
Fakt, że działa po ponownym załadowaniu przeglądarki bez pamięci podręcznej, wydaje się wskazywać, że wszystko jest dobrze po stronie S3 / CloudFront, w przeciwnym razie dlaczego treść miałaby być dostarczona. Ale dlaczego więc treść nie byłaby dostarczana przy pierwszym wyświetleniu strony?
Pracuję w Google Chrome na macOS. Firefox nie ma problemu z otrzymywaniem plików za każdym razem. Opera NIGDY nie pobiera plików. Safari zbierze obrazy po kilku odświeżeniach.
Korzystanie curl
Nie mam żadnych problemów:
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==
Niektórzy sugerują, że usunę dystrybucję CloudFront i odtworzę ją. Wydaje się to dość trudnym i niewygodnym rozwiązaniem.
Co powoduje ten problem?
Aktualizacja:
Dodanie nagłówków odpowiedzi z obrazu, którego nie udało się załadować.
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
źródło
Odpowiedzi:
Składasz dwa żądania dotyczące tego samego obiektu, jeden z HTML, jeden z XHR. Drugi kończy się niepowodzeniem, ponieważ Chrome używa buforowanej odpowiedzi z pierwszego żądania, które nie ma
Access-Control-Allow-Origin
nagłówka odpowiedzi.Czemu?
Błąd chromu 409090 Żądanie krzyżowego pochodzenia z pamięci podręcznej nie powiodło się po buforowaniu zwykłego żądania opisuje ten problem i jest to „nie można naprawić” - uważają, że ich zachowanie jest prawidłowe. Chrome uważa buforowaną odpowiedź za przydatną, najwyraźniej dlatego, że nie zawierała
Vary: Origin
nagłówka.Ale S3 nie zwraca,
Vary: Origin
gdy obiekt jest żądany bezOrigin:
nagłówka żądania, nawet gdy CORS jest skonfigurowany w segmencie.Vary: Origin
jest wysyłany tylko wtedy, gdyOrigin
nagłówek jest obecny w żądaniu.A CloudFront nie dodaje,
Vary: Origin
nawet gdyOrigin
jest na białej liście do przesyłania, co z definicji powinno oznaczać, że zmiana nagłówka może modyfikować odpowiedź - to jest powód, dla którego przekierowujesz i buforujesz nagłówki żądań.CloudFront dostaje przepustkę, ponieważ jego odpowiedź byłaby poprawna, gdyby S3 były bardziej poprawne, ponieważ CloudFront zwraca to, gdy jest dostarczone przez S3.
S3, trochę bardziej niepewny. Zwrot nie jest błędny,
Vary: Some-Header
gdy nie ma goSome-Header
w żądaniu.Oczywiście
Vary: Some-Absent-Header
jest poprawny, więc S3 byłby poprawny, gdyby dodałVary: Origin
do swojej odpowiedzi, jeśli skonfigurowano CORS, ponieważ to rzeczywiście mogłoby zmienić odpowiedź.Wygląda na to, że Chrome postąpiłby właściwie. Lub, jeśli nie zrobi to dobrze w tym przypadku, naruszałoby to
MUST NOT
. Z tej samej sekcji:Tak więc S3 naprawdę
SHOULD
powraca,Vary: Origin
gdy CORS jest skonfigurowany w segmencie, jeśliOrigin
nie ma go w żądaniu, ale tak nie jest.Mimo to S3 nie jest całkowicie zły, że nie zwraca nagłówka, ponieważ jest to tylko a
SHOULD
, a nieMUST
. Ponownie z tej samej sekcji RFC-7231:Z drugiej strony można argumentować, że Chrome powinien domyślnie wiedzieć, że zmiana
Origin
nagłówka powinna być kluczem pamięci podręcznej, ponieważ może zmienić odpowiedź w ten sam sposób,Authorization
może zmienić odpowiedź.Podobnie ponowne wykorzystanie w różnych krajach pochodzenia jest prawdopodobnie ograniczone charakterem,
Origin
ale ten argument nie jest mocny.tl; dr: Najwyraźniej nie można pomyślnie pobrać obiektu z HTML, a następnie pomyślnie pobrać go ponownie za pomocą żądania CORS z Chrome i S3 (z CloudFront lub bez), ze względu na specyfikę implementacji.
Obejście:
To zachowanie można obejść za pomocą CloudFront i Lambda @ Edge, używając następującego kodu jako wyzwalacza Origin Response.
To dodaje
Vary: Access-Control-Request-Headers, Access-Control-Request-Method, Origin
do każdej odpowiedzi z S3, która nie maVary
nagłówka. W przeciwnym razieVary
nagłówek w odpowiedzi nie zostanie zmodyfikowany.Uznanie autorstwa: Jestem także autorem oryginalnego postu na forach wsparcia AWS, gdzie ten kod został początkowo udostępniony.
Powyższe rozwiązanie Lambda @ Edge zapewnia w pełni poprawne zachowanie, ale oto dwie alternatywy, które mogą okazać się przydatne, w zależności od konkretnych potrzeb:
Alternative / Hackaround # 1: Wykuć nagłówki CORS w CloudFront.
CloudFront obsługuje niestandardowe nagłówki, które są dodawane do każdego żądania. Jeśli ustawisz
Origin:
dla każdego żądania, nawet te, które nie są pochodzenia krzyżowego, umożliwi to prawidłowe zachowanie w S3. Opcja konfiguracji nosi nazwę Nagłówki niestandardowego pochodzenia, a słowo „Pochodzenie” oznacza coś zupełnie innego niż oznacza w CORS. Skonfigurowanie takiego niestandardowego nagłówka w CloudFront zastępuje to, co jest wysyłane w żądaniu, określoną wartością lub dodaje go, jeśli jest nieobecny. Jeśli masz dokładnie jedno źródło dostępu do treści za pośrednictwem XHR, np.https://example.com
Możesz to dodać. Korzystanie*
jest wątpliwe, ale może działać w innych scenariuszach. Zastanów się dokładnie nad implikacjami.Alternative / Hackaround # 2: Użyj parametru „dummy” ciągu zapytania, który różni się dla HTML i XHR lub jest nieobecny w jednym lub drugim. Te parametry są zwykle nazywane,
x-*
ale nie powinnyx-amz-*
.Powiedzmy, że wymyśliłeś nazwę
x-request
. Tak<img src="https://dzczcexample.cloudfront.net/image.png?x-request=html">
. Podczas uzyskiwania dostępu do obiektu z JS nie dodawaj parametru zapytania. CloudFront już działa dobrze, buforując różne wersje obiektów, używającOrigin
nagłówka lub jego braku jako części klucza pamięci podręcznej, ponieważ przekazałeś ten nagłówek w swoim zachowaniu pamięci podręcznej. Problem w tym, że twoja przeglądarka tego nie wie. To przekonuje przeglądarkę, że w rzeczywistości jest to osobny obiekt, o który należy ponownie poprosić w kontekście CORS.Jeśli korzystasz z tych alternatywnych sugestii, użyj jednej lub drugiej - nie obu.
źródło
?x-some-key=some-value
parametru ciągu zapytania spowoduje przekonanie przeglądarki, że żądanie jest inne.Nie wiem, dlaczego otrzymujesz tak różne wyniki z różnych przeglądarek, ale:
Ten wiersz jest tym, co (jeśli możesz zwrócić ich uwagę) użyje CloudFront lub inżynier wsparcia, aby wykonać jedno z twoich nieudanych żądań. Jeśli żądanie dociera do serwera CloudFront, powinien mieć ten nagłówek w odpowiedzi. Jeśli tego nagłówka nie ma, to prawdopodobnie gdzieś nie powiedzie się, zanim dotrze do CloudFront.
źródło