Eu tenho uma página da Web ( https://smartystreets.com/contact ) que usa o jQuery para carregar alguns arquivos SVG do S3 através da CDN do CloudFront.
No Chrome, vou abrir uma janela de navegação anônima e o console. Então eu carregarei a página. À medida que a página é carregada, normalmente recebo de 6 a 8 mensagens no console com aparência semelhante a esta:
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.
Se eu fizer uma recarga padrão da página, mesmo várias vezes, continuarei recebendo os mesmos erros. Se o fizer Command+Shift+R
, a maioria e, às vezes, todas as imagens serão carregadas sem o XMLHttpRequest
erro.
Às vezes, mesmo após o carregamento das imagens, eu atualizo e uma ou mais imagens não são carregadas e retornamos esse XMLHttpRequest
erro novamente.
Verifiquei, alterei e verifiquei novamente as configurações do S3 e do Cloudfront. No S3, minha configuração do CORS se parece com isso:
<?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 tinha apenas o <AllowedOrigin>*</AllowedOrigin>
mesmo problema.)
Em CloudFront o comportamento de distribuição está definido para permitir que os métodos HTTP: GET, HEAD, OPTIONS
. Métodos em cache são os mesmos. Encaminhar cabeçalhos é definido como "Lista de permissões" e essa lista de permissões inclui "Cabeçalhos de solicitação de controle de acesso, Método de solicitação de controle de acesso, Origem".
O fato de funcionar após uma recarga de navegador sem cache parece indicar que tudo está bem no lado do S3 / CloudFront; caso contrário, por que o conteúdo seria entregue. Mas então por que o conteúdo não seria entregue na visualização de página inicial?
Estou trabalhando no Google Chrome no macOS. O Firefox não tem problemas em obter os arquivos todas as vezes. O Opera NUNCA obtém os arquivos. O Safari seleciona as imagens após várias atualizações.
Usando curl
eu não tenho problemas:
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==
Alguns sugeriram que eu exclua a distribuição do CloudFront e a recrie. Parece uma correção bastante dura e inconveniente.
O que está causando esse problema?
Atualizar:
Adicionando cabeçalhos de resposta de uma imagem que falhou ao carregar.
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
fonte
Respostas:
Você está fazendo duas solicitações para o mesmo objeto, uma em HTML e uma em XHR. O segundo falha, porque o Chrome usa a resposta em cache da primeira solicitação, que não tem
Access-Control-Allow-Origin
cabeçalho de resposta.Por quê?
Bug do Chromium 409090 A solicitação de origem cruzada do cache que falha após a solicitação regular é armazenada em cache descreve esse problema e é um "não corrigido" - eles acreditam que seu comportamento está correto. O Chrome considera a resposta em cache utilizável, aparentemente porque a resposta não incluiu um
Vary: Origin
cabeçalho.Mas o S3 não retorna
Vary: Origin
quando um objeto é solicitado sem umOrigin:
cabeçalho de solicitação, mesmo quando o CORS está configurado no bucket.Vary: Origin
é enviado apenas quando umOrigin
cabeçalho está presente na solicitação.E o CloudFront não adiciona
Vary: Origin
mesmo quandoOrigin
está na lista de permissões para encaminhamento, o que por definição significa que a variação do cabeçalho pode modificar a resposta - essa é a razão pela qual você encaminha e armazena em cache nos cabeçalhos de solicitação.O CloudFront recebe uma aprovação, porque sua resposta estaria correta se os S3 estivessem mais corretos, pois o CloudFront retorna isso quando é fornecido pelo S3.
S3, um pouco mais confuso. Não é errado retornar
Vary: Some-Header
quando não haviaSome-Header
na solicitação.Claramente,
Vary: Some-Absent-Header
é válido, portanto, o S3 estaria correto se adicionadoVary: Origin
à sua resposta se o CORS estiver configurado, pois isso poderia variar a resposta.E, aparentemente, isso faria o Chrome fazer a coisa certa. Ou, se não fizer a coisa certa nesse caso, estaria violando a
MUST NOT
. Da mesma seção:Portanto, o S3 realmente
SHOULD
retornaráVary: Origin
quando o CORS estiver configurado no bucket, seOrigin
estiver ausente da solicitação, mas não.Ainda assim, o S3 não está estritamente errado por não retornar o cabeçalho, porque é apenas um
SHOULD
, não umMUST
. Novamente, da mesma seção da RFC-7231:Por outro lado, pode-se argumentar que o Chrome deveria saber implicitamente que a variação do
Origin
cabeçalho deve ser uma chave de cache, pois pode alterar a resposta da mesma maneira queAuthorization
pode alterar a resposta.Da mesma forma, a reutilização através das origens é indiscutivelmente restrita pela natureza,
Origin
mas esse argumento não é forte.tl; dr: Você aparentemente não pode buscar com êxito um objeto do HTML e, em seguida, buscá-lo novamente com uma solicitação CORS no Chrome e S3 (com ou sem CloudFront), devido a peculiaridades nas implementações.
Solução alternativa:
Esse comportamento pode ser contornado com o CloudFront e o Lambda @ Edge, usando o código a seguir como um gatilho de resposta à origem.
Isso adiciona
Vary: Access-Control-Request-Headers, Access-Control-Request-Method, Origin
qualquer resposta do S3 que não tenhaVary
cabeçalho. Caso contrário, oVary
cabeçalho na resposta não será modificado.Atribuição: também sou o autor da postagem original nos fóruns de suporte da AWS em que esse código foi compartilhado inicialmente.
A solução Lambda @ Edge acima resulta em um comportamento totalmente correto, mas aqui estão duas alternativas que você pode achar úteis, dependendo de suas necessidades específicas:
Alternativa / Hackaround # 1: forjar os cabeçalhos CORS no CloudFront.
O CloudFront suporta cabeçalhos personalizados que são adicionados a cada solicitação. Se você definir
Origin:
todas as solicitações, mesmo aquelas que não são de origem cruzada, isso permitirá o comportamento correto no S3. A opção de configuração é chamada de Cabeçalhos de origem personalizados, com a palavra "Origem" significando algo totalmente diferente do que significa no CORS. A configuração de um cabeçalho personalizado como este no CloudFront substitui o que é enviado na solicitação pelo valor especificado ou o adiciona se ausente. Se você tem exatamente uma origem acessando seu conteúdo por XHR, por exemplohttps://example.com
, você pode adicioná-lo. O uso*
é duvidoso, mas pode funcionar para outros cenários. Considere as implicações cuidadosamente.Alternativa / Hackaround # 2: Use um parâmetro de seqüência de caracteres de consulta "fictício" que difere de HTML e XHR ou esteja ausente de um ou de outro. Esses parâmetros geralmente são nomeados,
x-*
mas não devem serx-amz-*
.Digamos que você invente o nome
x-request
. Então<img src="https://dzczcexample.cloudfront.net/image.png?x-request=html">
. Ao acessar o objeto a partir de JS, não adicione o parâmetro de consulta. O CloudFront já está fazendo a coisa certa, armazenando em cache diferentes versões dos objetos usando oOrigin
cabeçalho ou a ausência dele como parte da chave do cache, porque você encaminhou esse cabeçalho no comportamento do cache. O problema é que seu navegador não sabe disso. Isso convence o navegador de que este é realmente um objeto separado que precisa ser solicitado novamente, em um contexto CORS.Se você usar essas sugestões alternativas, use uma ou outra - não as duas.
fonte
?x-some-key=some-value
parâmetro de sequência de consulta arbitrária convencerá o navegador de que a solicitação é diferente.Não sei por que você obteria resultados tão diferentes em vários navegadores, mas:
Nessa linha, existe o que (se você conseguir chamar a atenção deles) que um engenheiro do CloudFront ou de suporte usará para seguir uma de suas solicitações com falha. Se a solicitação estiver chegando a um servidor CloudFront, ele deverá ter esse cabeçalho na resposta. Se esse cabeçalho não estiver lá, é provável que a solicitação falhe em algum lugar antes de chegar ao CloudFront.
fonte