Saya memiliki halaman web ( https://smartystreets.com/contact ) yang menggunakan jQuery untuk memuat beberapa file SVG dari S3 melalui CloudFront CDN.
Di Chrome saya akan membuka jendela Penyamaran serta konsol. Maka saya akan memuat halaman. Saat halaman dimuat, saya biasanya akan mendapatkan 6 hingga 8 pesan di konsol yang mirip dengan ini:
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.
Jika saya melakukan memuat ulang standar halaman, bahkan beberapa kali, saya terus mendapatkan kesalahan yang sama. Jika saya lakukan Command+Shift+R
maka sebagian besar, dan kadang-kadang semua, gambar akan dimuat tanpa XMLHttpRequest
kesalahan.
Kadang-kadang bahkan setelah gambar dimuat, saya akan menyegarkan dan satu atau lebih gambar tidak akan memuat dan mengembalikan XMLHttpRequest
kesalahan itu lagi.
Saya telah memeriksa, mengubah, dan memeriksa ulang pengaturan pada S3 dan Cloudfront. Pada S3 konfigurasi CORS saya terlihat seperti ini:
<?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>
(Catatan: awalnya hanya <AllowedOrigin>*</AllowedOrigin>
memiliki masalah yang sama.)
Dalam CloudFront perilaku distribusi diatur untuk memungkinkan Metode HTTP: GET, HEAD, OPTIONS
. Metode yang di-cache sama. Header Teruskan diatur ke "Daftar Putih" dan daftar putih itu termasuk, "Akses-Kontrol-Permintaan-Header, Akses-Kontrol-Permintaan-Metode, Asal".
Fakta bahwa ia berfungsi setelah browser memuat ulang tanpa cache tampaknya mengindikasikan bahwa semuanya baik-baik saja di sisi S3 / CloudFront, kalau tidak mengapa konten dikirimkan. Tapi mengapa konten tidak dikirim pada tampilan halaman awal?
Saya bekerja di Google Chrome di macOS. Firefox tidak memiliki masalah dalam mendapatkan file setiap saat. Opera TIDAK PERNAH mendapatkan file. Safari akan mengambil gambar setelah beberapa kali penyegaran.
Menggunakan curl
Saya tidak mendapatkan masalah:
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==
Beberapa menyarankan agar saya menghapus distribusi CloudFront dan membuatnya kembali. Sepertinya perbaikan yang agak keras dan tidak nyaman.
Apa yang menyebabkan masalah ini?
Memperbarui:
Menambahkan tajuk respons dari gambar yang gagal dimuat.
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
sumber
Jawaban:
Anda membuat dua permintaan untuk objek yang sama, satu dari HTML, satu dari XHR. Yang kedua gagal, karena Chrome menggunakan respons yang di-cache dari permintaan pertama, yang tidak memiliki
Access-Control-Allow-Origin
tajuk respons.Mengapa?
Bug Chromium 409090 Permintaan lintas asal dari cache gagal setelah permintaan reguler di-cache menjelaskan masalah ini, dan ini "tidak akan diperbaiki" - mereka yakin perilaku mereka benar. Chrome menganggap respons yang di-cache dapat digunakan, tampaknya karena respons tidak menyertakan
Vary: Origin
tajuk.Tetapi S3 tidak kembali
Vary: Origin
ketika suatu objek diminta tanpaOrigin:
header permintaan, bahkan ketika CORS dikonfigurasi pada bucket.Vary: Origin
hanya dikirim ketika adaOrigin
header dalam permintaan.Dan CloudFront tidak menambahkan
Vary: Origin
bahkan ketika masukOrigin
daftar putih untuk penerusan, yang seharusnya secara definisi berarti bahwa memvariasikan tajuk dapat mengubah respons - itulah alasan mengapa Anda meneruskan dan cache terhadap tajuk permintaan.CloudFront mendapat izin, karena responsnya akan benar jika S3 lebih benar, karena CloudFront mengembalikan ini ketika disediakan oleh S3.
S3, sedikit fuzzier. Tidak salah untuk kembali
Vary: Some-Header
ketika tidak adaSome-Header
permintaan.Jelas,
Vary: Some-Absent-Header
valid, jadi S3 akan benar jika ditambahkanVary: Origin
ke responsnya jika CORS dikonfigurasi, karena memang bisa beragam respons.Dan, tampaknya, ini akan membuat Chrome melakukan hal yang benar. Atau, jika tidak melakukan hal yang benar dalam kasus ini, itu akan melanggar a
MUST NOT
. Dari bagian yang sama:Jadi, S3 benar
SHOULD
- benar akan kembaliVary: Origin
ketika CORS dikonfigurasi pada bucket, jikaOrigin
tidak ada dari permintaan, tetapi tidak.Namun, S3 tidak sepenuhnya salah untuk tidak mengembalikan header, karena itu hanya a
SHOULD
, bukan aMUST
. Sekali lagi, dari bagian yang sama dari RFC-7231:Di sisi lain, argumen dapat dibuat bahwa Chrome harus secara implisit mengetahui bahwa memvariasikan
Origin
header harus menjadi kunci cache karena itu dapat mengubah respons dengan cara yang samaAuthorization
dapat mengubah respons.Demikian pula, penggunaan kembali lintas asal dapat diperdebatkan oleh sifat
Origin
tetapi argumen ini tidak kuat.tl; dr: Anda tampaknya tidak berhasil mengambil objek dari HTML dan kemudian berhasil mengambilnya kembali dengan permintaan CORS dengan Chrome dan S3 (dengan atau tanpa CloudFront), karena kekhasan dalam implementasi.
Penanganan masalah:
Perilaku ini dapat diatasi dengan CloudFront dan Lambda @ Edge, menggunakan kode berikut sebagai pemicu Respons Asal.
Ini menambah
Vary: Access-Control-Request-Headers, Access-Control-Request-Method, Origin
respons apa pun dari S3 yang tidak memilikiVary
tajuk. Jika tidak,Vary
tajuk dalam respons tidak diubah.Atribusi: Saya juga penulis posting asli di forum Dukungan AWS tempat kode ini awalnya dibagikan.
Solusi Lambda @ Edge di atas menghasilkan perilaku yang sepenuhnya benar, tetapi berikut adalah dua alternatif yang mungkin berguna bagi Anda, tergantung pada kebutuhan spesifik Anda:
Alternatif / Hackaround # 1: Tempa header CORS di CloudFront.
CloudFront mendukung header kustom yang ditambahkan ke setiap permintaan. Jika Anda menetapkan
Origin:
pada setiap permintaan, bahkan yang tidak berasal dari silang, ini akan memungkinkan perilaku yang benar dalam S3. Opsi konfigurasi disebut Custom Origin Header, dengan kata "Origin" yang berarti sesuatu yang sama sekali berbeda dari artinya di CORS. Mengkonfigurasi tajuk khusus seperti ini di CloudFront menimpa apa yang dikirim dalam permintaan dengan nilai yang ditentukan, atau menambahkannya jika tidak ada. Jika Anda memiliki satu asal tepat mengakses konten Anda melalui XHR, misalnyahttps://example.com
, Anda dapat menambahkannya. Penggunaan*
meragukan, tetapi mungkin berhasil untuk skenario lain. Pertimbangkan implikasinya dengan cermat.Alternatif / Peretasan # 2: Gunakan parameter string kueri "dummy" yang berbeda untuk HTML dan XHR atau tidak ada satu atau yang lain. Parameter ini biasanya dinamai
x-*
tetapi tidak bolehx-amz-*
.Katakanlah Anda membuat nama
x-request
. Jadi<img src="https://dzczcexample.cloudfront.net/image.png?x-request=html">
. Saat mengakses objek dari JS, jangan tambahkan parameter kueri. CloudFront sudah melakukan hal yang benar, dengan melakukan caching berbagai versi objek menggunakanOrigin
header atau tidak adanya itu sebagai bagian dari kunci cache, karena Anda meneruskan header itu dalam perilaku cache Anda. Masalahnya, browser Anda tidak mengetahui hal ini. Ini meyakinkan browser bahwa ini sebenarnya adalah objek terpisah yang perlu diminta lagi, dalam konteks CORS.Jika Anda menggunakan saran alternatif ini, gunakan satu atau yang lain - tidak keduanya.
sumber
?x-some-key=some-value
parameter string kueri arbitrer akan meyakinkan browser bahwa permintaannya berbeda.Saya tidak tahu mengapa Anda akan mendapatkan hasil yang berbeda dari berbagai browser, tetapi:
Baris itu ada apa (jika Anda bisa mendapatkan perhatian mereka) seorang insinyur CloudFront atau Dukungan akan digunakan untuk mengikuti salah satu permintaan Anda yang gagal. Jika permintaan menuju ke server CloudFront, ia harus memiliki header ini sebagai respons. Jika tajuk itu tidak ada, maka permintaan kemungkinan gagal di suatu tempat sebelum sampai ke CloudFront.
sumber