Tengo una página web ( https://smartystreets.com/contact ) que usa jQuery para cargar algunos archivos SVG desde S3 a través de CloudFront CDN.
En Chrome abriré una ventana de incógnito, así como la consola. Luego cargaré la página. A medida que se carga la página, normalmente recibo de 6 a 8 mensajes en la consola que se parecen a esto:
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.
Si realizo una recarga estándar de la página, incluso varias veces, sigo recibiendo los mismos errores. Si lo hago Command+Shift+R
, la mayoría de las imágenes, y a veces todas, se cargarán sin el XMLHttpRequest
error.
A veces, incluso después de que las imágenes se hayan cargado, actualizaré y una o más de las imágenes no se cargarán y devolverán ese XMLHttpRequest
error nuevamente.
He verificado, cambiado y vuelto a comprobar la configuración en S3 y Cloudfront. En S3 mi configuración CORS se ve así:
<?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>
(Nota: inicialmente solo tenía el <AllowedOrigin>*</AllowedOrigin>
mismo problema).
En CloudFront el comportamiento de distribución está configurado para permitir los métodos HTTP: GET, HEAD, OPTIONS
. Los métodos en caché son los mismos. Reenviar encabezados se establece en "Lista blanca" y esa lista blanca incluye, "Acceso-Control-Solicitud-Encabezados, Acceso-Control-Solicitud-Método, Origen".
El hecho de que funcione después de una recarga del navegador sin caché parece indicar que todo está bien en el lado S3 / CloudFront, de lo contrario, ¿por qué se entregaría el contenido? Pero entonces, ¿por qué no se entregaría el contenido en la vista de página inicial?
Estoy trabajando en Google Chrome en macOS. Firefox no tiene problemas para obtener los archivos cada vez. Opera NUNCA obtiene los archivos. Safari recogerá las imágenes después de varias actualizaciones.
Usando curl
no tengo ningún problema:
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==
Algunos han sugerido que elimine la distribución de CloudFront y la vuelva a crear. Parece una solución bastante dura e inconveniente.
que esta causando este problema?
Actualizar:
Agregar encabezados de respuesta de una imagen que no se pudo cargar.
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
fuente
Respuestas:
Estás haciendo dos solicitudes para el mismo objeto, una de HTML, otra de XHR. El segundo falla, porque Chrome usa la respuesta en caché de la primera solicitud, que no tiene
Access-Control-Allow-Origin
encabezado de respuesta.¿Por qué?
El error de Chromium 409090 La solicitud de origen cruzado del error de caché después de que la solicitud regular se almacena en caché describe este problema, y es un "no se solucionará": creen que su comportamiento es correcto. Chrome considera que la respuesta almacenada en caché es utilizable, aparentemente porque la respuesta no incluyó un
Vary: Origin
encabezado.Pero S3 no regresa
Vary: Origin
cuando se solicita un objeto sin unOrigin:
encabezado de solicitud, incluso cuando CORS está configurado en el depósito.Vary: Origin
solo se envía cuando hay unOrigin
encabezado presente en la solicitud.Y CloudFront no agrega
Vary: Origin
incluso cuandoOrigin
está en la lista blanca para el reenvío, lo que por definición debería significar que variar el encabezado podría modificar la respuesta; esa es la razón por la que reenvía y almacena en caché los encabezados de solicitud.CloudFront obtiene un pase, porque su respuesta sería correcta si los S3 fueran más correctos, ya que CloudFront lo devuelve cuando lo proporciona S3.
S3, un poco más borroso. No es incorrecto regresar
Vary: Some-Header
cuando no habíaSome-Header
en la solicitud.Claramente,
Vary: Some-Absent-Header
es válido, por lo que S3 sería correcto si se agregaVary: Origin
a su respuesta si CORS está configurado, ya que eso podría variar la respuesta.Y, aparentemente, esto haría que Chrome hiciera lo correcto. O, si no hace lo correcto en este caso, estaría violando a
MUST NOT
. De la misma sección:Entonces, S3 realmente
SHOULD
regresaráVary: Origin
cuando CORS esté configurado en el bucket, siOrigin
está ausente de la solicitud, pero no lo hace.Aún así, S3 no está estrictamente equivocado para no devolver el encabezado, porque es solo un
SHOULD
, no unMUST
. De nuevo, de la misma sección de RFC-7231:Por otro lado, se podría argumentar que Chrome debería saber implícitamente que variar el
Origin
encabezado debería ser una clave de caché porque podría cambiar la respuesta de la misma manera queAuthorization
podría cambiar la respuesta.Del mismo modo, la reutilización en todos los orígenes está posiblemente limitada por la naturaleza de,
Origin
pero este argumento no es fuerte.tl; dr: aparentemente no puede recuperar un objeto de HTML con éxito y luego recuperarlo nuevamente con una solicitud CORS con Chrome y S3 (con o sin CloudFront), debido a peculiaridades en las implementaciones.
Solución alternativa:
Este comportamiento se puede solucionar con CloudFront y Lambda @ Edge, utilizando el siguiente código como desencadenador de respuesta de origen.
Esto se suma
Vary: Access-Control-Request-Headers, Access-Control-Request-Method, Origin
a cualquier respuesta de S3 que no tengaVary
encabezado. De lo contrario, elVary
encabezado en la respuesta no se modifica.Atribución: también soy el autor de la publicación original en los foros de soporte de AWS donde este código se compartió inicialmente.
La solución Lambda @ Edge anterior da como resultado un comportamiento completamente correcto, pero aquí hay dos alternativas que puede encontrar útiles, según sus necesidades específicas:
Alternativa / Hackaround # 1: falsifique los encabezados CORS en CloudFront.
CloudFront admite encabezados personalizados que se agregan a cada solicitud. Si configura
Origin:
cada solicitud, incluso aquellas que no son de origen cruzado, esto permitirá un comportamiento correcto en S3. La opción de configuración se llama Encabezados de origen personalizados, con la palabra "Origen" que significa algo completamente diferente de lo que significa en CORS. La configuración de un encabezado personalizado como este en CloudFront sobrescribe lo que se envía en la solicitud con el valor especificado, o lo agrega si está ausente. Si tiene exactamente un origen que accede a su contenido a través de XHR, por ejemplohttps://example.com
, puede agregarlo. Usar*
es dudoso, pero podría funcionar para otros escenarios. Considere las implicaciones cuidadosamente.Alternativa / Hackaround # 2: Use un parámetro de cadena de consulta "ficticio" que difiera para HTML y XHR o esté ausente de uno u otro. Estos parámetros generalmente se nombran
x-*
pero no deberíanx-amz-*
.Digamos que inventas el nombre
x-request
. Por lo tanto<img src="https://dzczcexample.cloudfront.net/image.png?x-request=html">
. Al acceder al objeto desde JS, no agregue el parámetro de consulta. CloudFront ya está haciendo lo correcto, al almacenar en caché diferentes versiones de los objetos utilizando elOrigin
encabezado o la ausencia de este como parte de la clave de caché, porque reenvió ese encabezado en su comportamiento de caché. El problema es que su navegador no lo sabe. Esto convence al navegador de que este es en realidad un objeto separado que debe solicitarse nuevamente, en un contexto CORS.Si usa estas sugerencias alternativas, use una u otra, no ambas.
fuente
?x-some-key=some-value
parámetro de cadena de consulta arbitraria convencerá al navegador de que la solicitud es diferente.No sé por qué obtendría resultados tan diferentes de varios navegadores, pero:
Esa línea justo allí es lo que (si puede llamar su atención) usará un ingeniero de CloudFront o de Soporte para seguir una de sus solicitudes fallidas. Si la solicitud llega a un servidor CloudFront, debe tener este encabezado en la respuesta. Si ese encabezado no está allí, es probable que la solicitud falle en algún lugar antes de llegar a CloudFront.
fuente