Chrome S3 क्लाउडफ्रंट: आरंभिक XHR अनुरोध पर कोई 'एक्सेस-कंट्रोल-अलाउंस-ओरिजिन' हेडर नहीं


30

मेरे पास एक वेबपेज ( https://smartystreets.com/contact ) है जो क्लाउड 3 सीडीएन के माध्यम से एस 3 से कुछ एसवीजी फ़ाइलों को लोड करने के लिए jQuery का उपयोग करता है।

Chrome में मैं एक गुप्त विंडो के साथ-साथ कंसोल खोलूंगा। फिर मैं पेज लोड करूंगा। पृष्ठ लोड होने के साथ, मुझे आमतौर पर कंसोल में 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 Methods को अनुमति देने के लिए वितरण व्यवहार सेट किया गया है GET, HEAD, OPTIONS:। कैश्ड तरीके समान हैं। फ़ॉरवर्ड हेडर्स "व्हाइटलिस्ट" पर सेट है और उस वाइटलिस्ट में "एक्सेस-कंट्रोल-रिक्वेस्ट-हेडर्स, एक्सेस-कंट्रोल-रिक्वेस्ट-मेथड, ओरिजिन" शामिल है।

तथ्य यह है कि यह कैश-कम ब्राउज़र रीलोड के बाद काम करता है, यह इंगित करता है कि सभी S3 / CloudFront साइड पर अच्छी तरह से है, अन्यथा सामग्री वितरित क्यों की जाएगी। लेकिन फिर प्रारंभिक पृष्ठ-दृश्य पर सामग्री क्यों नहीं दी जाएगी?

मैं macOS पर Google Chrome में काम कर रहा हूं। फ़ायरफ़ॉक्स को हर बार फ़ाइलें प्राप्त करने में कोई समस्या नहीं होती है। ओपेरा कभी फ़ाइलों को प्राप्त करता है। सफारी कई ताज़ा करने के बाद छवियों को उठाएगा।

उपयोग करने से 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

आप सही हैं - हटाएं और पुन: बनाएँ चरम है और बस कभी भी आवश्यक नहीं होना चाहिए। क्या आप हमें असफल अनुरोध के लिए ब्राउज़र का अनुरोध और प्रतिक्रिया हेडर दिखा सकते हैं? और शायद एक ही वस्तु के सफल अनुरोध के लिए?
माइकल - sqlbot

@ माइकल- sqlbot, मैं उम्मीद कर रहा था कि आप URL ( smartystreets.com/contact ) पर जाएँगे और देखें कि आपकी मशीन पर भी वही काम हो रहा है या नहीं। :) त्रुटियों के बारे में दिलचस्प बात यह है कि कंसोल में त्रुटि से अलग, ब्राउज़र 200 की स्थिति की रिपोर्ट करता है, यह कहते हुए कि यह छवि का उपयोग कर रहा है "(डिस्क कैश से)", जो कि गुप्त के साथ संभव नहीं होना चाहिए, मैं विचार। स्थानीय कैश साफ़ करने के बाद भी।
सनस्पार्क

1
हाँ, लोग अक्सर "मेक अप" डोमेन नाम बनाते हैं (जो वास्तविक साइटें बनते हैं, लेकिन प्रश्न में साइट नहीं) जो मुझे शुरू में एहसास नहीं हुआ था कि आपने अपनी साइट के लिए वास्तविक, सही लिंक दिया था। इसके लिए धन्यवाद, आप मेरे अनुरोध की अवहेलना कर सकते हैं। मैं समस्या की नकल कर सकता हूं। यह क्लाइंट साइड समस्या की तरह लगता है। मैं एक सिद्धांत का पीछा कर रहा हूं।
माइकल - sqlbot

मुझे लगता है कि आप क्लाइंट-साइड इश्यू होने के बारे में सही हो सकते हैं। छवियों को HTML में A टैग के साथ जोड़ा जाता है और फिर ऐसा लगता है कि जैसे उन्हें jQuery में फिर से अनुरोध किया गया है। शायद त्रुटि एक कॉल से है और 200 दूसरे से है।
SunSparc

1
मैं वास्तव में ऐसा ही मानता हूं। Chrome और S3 एक ऐसे तरीके से बातचीत कर रहे हैं जो एक CORS अनुरोध को तोड़ता है जो एक ही वस्तु के लिए गैर-CORS अनुरोध का अनुसरण करता है। यकीनन, दोनों गलत हैं ... लेकिन यकीनन, दोनों में से कोई भी गलत नहीं है। मुझे नहीं लगता कि आप ऑब्जेक्ट की दो प्रतियों को अलग-अलग कुंजी के साथ संग्रहीत किए बिना इसे ठीक कर सकते हैं ... या दो अलग-अलग CloudFront वितरण (अलग होस्टनाम) का उपयोग करके ताकि आप दोनों CORS और गैर- CORS अनुरोध न करें। यदि आप चाहें, तो मैं इस निष्कर्ष पर लिखूंगा कि मैं इस निष्कर्ष पर कैसे पहुंच रहा हूं।
माइकल - sqlbot

जवाबों:


55

आप एक ही वस्तु के लिए दो अनुरोध कर रहे हैं, एक HTML से, एक XHR से। दूसरा एक विफल हो जाता है, क्योंकि क्रोम पहले अनुरोध से कैश्ड प्रतिक्रिया का उपयोग करता है, जिसमें कोई Access-Control-Allow-Originप्रतिक्रिया शीर्ष लेख नहीं है।

क्यूं कर?

क्रोमियम बग 409090 नियमित अनुरोध के कैश होने के बाद कैश से क्रॉस-मूल अनुरोध इस समस्या का वर्णन करता है, और यह "ठीक नहीं होगा" - उनका मानना ​​है कि उनका व्यवहार सही है। क्रोम कैश्ड प्रतिक्रिया को प्रयोग करने योग्य मानता है, जाहिरा तौर पर क्योंकि प्रतिक्रिया में Vary: Originहेडर शामिल नहीं था ।

लेकिन S3 वापस नहीं आता है Vary: Originजब कोई वस्तु Origin:अनुरोध हेडर के बिना मांगी जाती है , तब भी जब कोर्सेट बाल्टी पर कॉन्फ़िगर किया गया हो। Vary: Originकेवल तभी भेजा जाता है जब Originअनुरोध में कोई हेडर मौजूद हो।

और CloudFront को अग्रेषित करने के लिए श्वेत Vary: Originहोने पर भी नहीं जोड़ा Originजाता है, जिसे परिभाषा के अनुसार इसका मतलब यह होना चाहिए कि शीर्ष लेख को अलग करने से प्रतिक्रिया संशोधित हो सकती है - यही कारण है कि आप अनुरोध हेडर के खिलाफ आगे और कैश करते हैं।

CloudFront को एक पास मिलता है, क्योंकि S3 के सही होने पर इसकी प्रतिक्रिया सही होगी, क्योंकि S3 द्वारा प्रदान किए जाने पर CloudFront इसे वापस कर देता है।

एस 3, थोड़ा फजीर। यह है गलत नहीं लौटने के लिए Vary: Some-Headerजब वहाँ कोई था Some-Headerअनुरोध में।

उदाहरण के लिए, एक प्रतिक्रिया जिसमें शामिल है

Vary: accept-encoding, accept-language

इंगित करता है कि मूल सर्वर ने इस प्रतिक्रिया के लिए सामग्री का चयन करते समय कारकों के निर्धारण के रूप में अनुरोध Accept-Encodingऔर Accept-Languageफ़ील्ड (या इसके अभाव) का उपयोग किया हो सकता है । (महत्व दिया)

https://tools.ietf.org/html/rfc7231#section-7.1.4

स्पष्ट रूप से, Vary: Some-Absent-Headerमान्य है, इसलिए यदि यह Vary: Originकॉर्स कॉन्फ़िगर किया गया है, तो S3 सही होगा , क्योंकि यह प्रतिक्रिया वास्तव में भिन्न हो सकती है।

और, जाहिर है, यह क्रोम को सही काम करेगा। या, अगर यह इस मामले में सही काम नहीं करता है, तो यह उल्लंघन होगा MUST NOT। उसी सेक्शन से:

एक मूल सर्वर Varyदो उद्देश्यों के लिए फ़ील्ड की सूची के साथ भेज सकता है:

  1. कैश प्राप्तकर्ताओं को सूचित करने के लिए कि वे MUST NOTइस प्रतिक्रिया का उपयोग बाद के अनुरोध को संतुष्ट करने के लिए करते हैं जब तक कि बाद के अनुरोध में सूचीबद्ध फ़ील्ड के लिए मूल अनुरोध (RFC7234] की धारा 4.1 के समान मान न हों। दूसरे शब्दों में, वैरी संग्रहीत कैश प्रविष्टि में एक नए अनुरोध से मिलान करने के लिए आवश्यक कैश कुंजी का विस्तार करता है।

...

तो, S3 वास्तव SHOULDमें वापस आ रहा है Vary: Originजब CORS बाल्टी पर कॉन्फ़िगर किया गया है, अगर Originअनुरोध से अनुपस्थित है, लेकिन यह नहीं है।

फिर भी, हेडर नहीं लौटाने के लिए S3 कड़ाई से गलत नहीं है, क्योंकि यह केवल a है SHOULD, a नहीं MUST। फिर, RFC-7231 के एक ही खंड से:

एक मूल सर्वर SHOULDएक वैरी हेडर फ़ील्ड भेजता है जब एक प्रतिनिधित्व का चयन करने के लिए इसका एल्गोरिथ्म विधि और अनुरोध लक्ष्य, ... के अलावा अनुरोध संदेश के पहलुओं के आधार पर भिन्न होता है

दूसरी ओर, यह तर्क दिया जा सकता है कि क्रोम को स्पष्ट रूप से पता होना चाहिए कि Originहेडर को बदलना एक कैश कुंजी होना चाहिए क्योंकि यह उसी तरह Authorizationसे प्रतिक्रिया को बदल सकता है जिस तरह से प्रतिक्रिया बदल सकती है।

... जब तक विचरण को पार नहीं किया जा सकता है या कैश पारदर्शिता को रोकने के लिए मूल सर्वर को जानबूझकर कॉन्फ़िगर नहीं किया गया है। उदाहरण के लिए, Authorizationफ़ील्ड नाम भेजने की कोई आवश्यकता नहीं है Varyक्योंकि उपयोगकर्ताओं के बीच पुन: उपयोग क्षेत्र की परिभाषा से विवश है [...]

इसी तरह, उत्पत्ति के दौरान पुन: उपयोग यकीनन प्रकृति की विवशता है Originलेकिन यह तर्क मजबूत नहीं है।


tl; dr: आप जाहिरा तौर पर HTML से किसी ऑब्जेक्ट को सफलतापूर्वक प्राप्त नहीं कर सकते हैं और फिर इसे क्रियान्वयन में ख़ासियत के कारण Chrome और S3 (CloudFront के साथ या बिना) के साथ कोरस अनुरोध के रूप में सफलतापूर्वक प्राप्त कर सकते हैं।


युक्ति:

इस व्यवहार को CloudFront और Lambda @ Edge के साथ काम किया जा सकता है, मूल कोड ट्रिगर के रूप में निम्न कोड का उपयोग कर सकता है।

यह Vary: Access-Control-Request-Headers, Access-Control-Request-Method, OriginS3 से किसी भी प्रतिक्रिया के लिए जोड़ता है जिसमें कोई Varyहेडर नहीं है । अन्यथा, Varyप्रतिक्रिया में शीर्ष लेख संशोधित नहीं है।

'use strict';

// If the response lacks a Vary: header, fix it in a CloudFront Origin Response trigger.

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    if (!headers['vary'])
    {
        headers['vary'] = [
            { key: 'Vary', value: 'Access-Control-Request-Headers' },
            { key: 'Vary', value: 'Access-Control-Request-Method' },
            { key: 'Vary', value: 'Origin' },
        ];
    }
    callback(null, response);
};

विशेषता: मैं AWS सपोर्ट फ़ोरम पर मूल पोस्ट का लेखक भी हूँ जहाँ इस कोड को शुरू में साझा किया गया था।


उपरोक्त लमबा @ एज समाधान पूरी तरह से सही व्यवहार में परिणाम देता है, लेकिन यहां दो विकल्प हैं जो आपको अपनी विशिष्ट आवश्यकताओं के आधार पर उपयोगी मिल सकते हैं:

वैकल्पिक / हैकअरेक्शन # 1: CloudFront में कॉर्स हेडर्स फोर्ज करें।

CloudFront कस्टम हेडर का समर्थन करती है जो प्रत्येक अनुरोध में जोड़े जाते हैं। यदि आप Origin:प्रत्येक अनुरोध पर सेट करते हैं, यहां तक ​​कि जो क्रॉस-मूल नहीं हैं, तो यह S3 में सही व्यवहार को सक्षम करेगा। कॉन्फ़िगरेशन ऑप्शन को कस्टम ओरिजिनल हेडर्स कहा जाता है, जिसका अर्थ है "ओरिजिनल" शब्द का अर्थ है कॉर्स में इसका मतलब पूरी तरह से अलग। CloudFront में इस तरह एक कस्टम हेडर को कॉन्फ़िगर करना निर्दिष्ट मूल्य के साथ अनुरोध में भेजे गए को अधिलेखित करता है, या अनुपस्थित होने पर इसे जोड़ता है। यदि आपके पास वास्तव में एक मूल एक्सएचआर, जैसे अधिक आपकी सामग्री तक पहुंचने https://example.comके लिए, आप है कि जोड़ सकते हैं। उपयोग करना *संदिग्ध है, लेकिन अन्य परिदृश्यों के लिए काम कर सकता है। निहितार्थ पर ध्यान से विचार करें।

वैकल्पिक / हैकअरेक्शन # 2: एक "डमी" क्वेरी स्ट्रिंग पैरामीटर का उपयोग करें जो HTML और XHR के लिए अलग-अलग है या एक या दूसरे से अनुपस्थित है। इन मापदंडों को आमतौर पर नाम दिया गया है x-*लेकिन ऐसा नहीं होना चाहिए x-amz-*

मान लीजिए कि आप नाम बनाते हैं x-request। तो <img src="https://dzczcexample.cloudfront.net/image.png?x-request=html">। JS से ऑब्जेक्ट एक्सेस करते समय, क्वेरी पैरामीटर न जोड़ें। CloudFront पहले से ही सही काम कर रही है, Originशीर्ष लेख या कैश अनुपस्थिति के भाग के रूप में इसका उपयोग करने वाले ऑब्जेक्ट के विभिन्न संस्करणों को कैशिंग करके , क्योंकि आपने उस शीर्षलेख को अपने कैश व्यवहार में अग्रेषित किया है। समस्या यह है, आपके ब्राउज़र को यह पता नहीं है। यह ब्राउज़र को आश्वस्त करता है कि यह वास्तव में एक अलग वस्तु है जिसे फिर से अनुरोध करने की आवश्यकता है, एक कोर संदर्भ में।

यदि आप इन वैकल्पिक सुझावों का उपयोग करते हैं, तो एक या दूसरे का उपयोग करें - दोनों नहीं।


5
आपकी प्रतिक्रिया एक जीवनरक्षक, महान उत्तर है। आपने मुझे कुछ गंभीर समय बचाया।
११ अक्टूबर को

नमस्ते, मैं अपने s3 के लिए क्लाउडफ्रंट का उपयोग नहीं करता हूं, इसलिए यह समाधान मदद नहीं कर रहा है, क्या कुछ और है जो मैं कर सकता हूं?
जेफिन

1
@ जेफिन, वैकल्पिक # 2 ऊपर S3 के लिए अकेले काम करेगा, बिना CloudFront के। एक मनमाना ?x-some-key=some-valueक्वेरी स्ट्रिंग पैरामीटर जोड़ना ब्राउज़र को आश्वस्त करेगा कि अनुरोध अलग है।
माइकल - sqlbot

1
@ माइकल-sqlbot: हाँ, एक आकर्षण की तरह काम किया
Jeffin

1
@ लियोनेल हाँ, यह सही लगता है।
माइकल - sqlbot

1

मुझे नहीं पता कि आपको विभिन्न ब्राउज़रों से ऐसे भिन्न परिणाम क्यों प्राप्त होंगे, लेकिन:

X-Amz-Cf-Id: wxn_m9meR6yPoyyvj1R7x83pBDPJy1nT7kdMv1aMwXVtHCunT9OC9x ==

वह रेखा वहीं है (यदि आप उनका ध्यान आकर्षित कर सकते हैं) CloudFront या Support Engineer आपके किसी असफल अनुरोध का पालन करने के लिए उपयोग करेगा। यदि अनुरोध CloudFront सर्वर को मिल रहा है, तो प्रतिक्रिया में इसका हेडर होना चाहिए। यदि वह हेडर नहीं है, तो क्लाउडफ़ॉरेस्ट में जाने से पहले अनुरोध कहीं न कहीं विफल हो रहा है।


धन्यवाद, मैं देखूंगा कि क्या मुझे AWS मंचों पर कोई प्रतिक्रिया मिल सकती है।
सनस्पार्क

1
आपको डेवलपर सहायता के लिए $ 29 का भुगतान करना पड़ सकता है। यह किसी भी व्यवसाय के लिए एक मामूली राशि है, यह देखते हुए कि किसी व्यक्ति का समय कितना खर्च होता है।
टिम

1
@ ध्यान दें, डेवलपर का समर्थन केवल $ 29 नहीं है। यही बेस प्राइस है। यदि आपके मासिक AWS बिल का 3%> = $ 29 है, तो आप आधार के बजाय 3% का भुगतान करते हैं।
माइकल - sqlbot

धन्यवाद @ माइकल- sqlbot, मुझे यह महसूस नहीं हुआ। मुझे पता है कि आपके पास आरक्षित इंस्टेंस जैसी चीजें होने पर समर्थन मूल्य जल्दी जुड़ सकता है, लेकिन जब आपके पास बहुत सारे संसाधन होते हैं, तो मैंने डेवलपर मूल्य निर्धारण पर ध्यान नहीं दिया है।
टिम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.