Carregamento de fontes entre domínios Amazon S3 CORS (compartilhamento de recursos entre origens) e Firefox

134

Há um problema antigo: o Firefox não carregava fontes de origem diferente da página atual. Geralmente, o problema surge quando as fontes são veiculadas nas CDNs.

Várias soluções foram levantadas em outras questões:

CSS @ font-face não funciona no Firefox, mas no Chrome e IE

Com a introdução do Amazon S3 CORS, existe uma solução usando o CORS para solucionar o problema de carregamento de fontes no Firefox?

editar: Seria ótimo ver uma amostra da configuração do S3 CORS.

edit2: Encontrei uma solução funcional sem realmente entender o que ela fez. Se alguém puder fornecer explicações mais detalhadas sobre as configurações e a mágica de segundo plano que ocorre na interpretação da Amazon pela configuração, isso será muito apreciado, como acontece com nzifnab, que oferece uma recompensa por isso.

VKen
fonte

Respostas:

148

Atualização 10 de setembro de 2014:

Você não precisa mais executar nenhum dos hacks de seqüência de caracteres de consulta abaixo, já que o Cloudfront agora suporta o CORS corretamente. Consulte http://aws.amazon.com/blogs/aws/enhanced-cloudfront-customization/ e esta resposta para obter mais informações: https://stackoverflow.com/a/25305915/308315


OK, finalmente consegui que as fontes funcionassem usando a configuração abaixo com alguns ajustes nos exemplos da documentação.

Minhas fontes estão hospedadas no S3, mas estão à frente do cloudfront.

Eu não tenho certeza por que ela funciona, o meu palpite é provavelmente que o <AllowedMethod> GETe <AllowedHeader> Content-*é necessário.

Se alguém proficiente na configuração do Amazon S3 CORS puder esclarecer isso, será muito apreciado.

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>https://mydomain.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Content-*</AllowedHeader>
        <AllowedHeader>Host</AllowedHeader>
    </CORSRule>
    <CORSRule>
        <AllowedOrigin>https://*.mydomain.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Content-*</AllowedHeader>
        <AllowedHeader>Host</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

editar:

Alguns desenvolvedores estão enfrentando problemas do Cloudfront armazenando em cache o Access-Control-Allow-Origincabeçalho. Esse problema foi solucionado pela equipe da AWS no link ( https://forums.aws.amazon.com/thread.jspa?threadID=114646 ) abaixo, comentado por @ Jeff-Atwood.

No encadeamento vinculado, é recomendável, como solução alternativa, usar uma Cadeia de caracteres de consulta para diferenciar chamadas de domínios diferentes. Vou reproduzir o exemplo abreviado aqui.

Usando curlpara verificar os cabeçalhos de resposta:

Domínio A: a.domain.com

curl -i -H "Origin: https://a.domain.com" http://hashhashhash.cloudfront.net/font.woff?https_a.domain.com

Cabeçalhos de resposta do domínio A:

Access-Control-Allow-Origin: https://a.domain.com
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000
Access-Control-Allow-Credentials: true
X-Cache: Miss from Cloudfront

Domínio B: b.domínio.com

curl -i -H "Origin: http://b.domain.com" http://hashhashhash.cloudfront.net/font.woff?http_b.domain.com

Cabeçalhos de resposta do domínio B:

Access-Control-Allow-Origin: http://b.domain.com
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000
Access-Control-Allow-Credentials: true
X-Cache: Miss from Cloudfront

Você notará Access-Control-Allow-Originque retornou valores diferentes, que passaram pelo cache do Cloudfront.

VKen
fonte
2
você teve problemas semelhantes ao descrito aqui - o Access-Control-Allow-Origincabeçalho é armazenado em cache e invalida o CORS quando uma solicitação subsequente é feita através de um subdomínio diferente?
ov
1
@ov Não tenho o problema, pois defino explicitamente os domínios que usam os recursos. Eu li o link que você postou antes. Lembrei-me vagamente de algumas respostas em outro segmento dizendo que os domínios precisam ser explicitamente declarados, portanto, <AllowedOrigin> * </AllowedOrigin> não é realmente permitido, devido a algumas restrições. Não consigo encontrar essas postagens de resposta agora, pode ser uma postagem no blog que li em outro lugar. Espero que ajude.
VKen 1/10/12
3
Você pode ter vários elementos AllowedOrigin dentro de um único elemento CORSRule, para poder combinar essas CORSRules em um único elemento, pois os outros elementos são idênticos.
Ben Hull
4
@dan se o bucket S3 for servido pelo CloudFront, parece que a resposta é variar a cadeia de consulta
Jeff Atwood
2
Este tem sido um problema extremamente frustrante. A boa notícia é que agora o S3 parece estar fazendo a coisa certa; portanto, pelo menos é possível servir tudo que não seja webfonts através do CloudFront e servir os arquivos de fonte diretamente do S3. Infelizmente, o hack da string de consulta não é realmente prático em nosso aplicativo sem refatoração mais significativa, pois os ativos são todos atendidos pelo pipeline de ativos do Rails e não há maneira conveniente de ajustar os URLs dos ativos no momento da solicitação (todos eles são gerados durante a implantação quando os ativos são pré-compilados). O URL da fonte em css já está no S3.
amigos estão dizendo sobre zach
97

Depois de alguns ajustes, parece que consegui que isso funcionasse sem o hack da string de consulta. Mais informações aqui: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorS3Origin.html#RequestS3-cors

Vou percorrer toda a minha configuração para que seja fácil ver o que fiz, espero que isso ajude outras pessoas.

Informações básicas: estou usando um aplicativo Rails que possui a gema asset_sync para colocar ativos no S3. Isso inclui fontes.

No console do S3, cliquei no meu bucket, propriedades e 'editar configuração de cors', aqui: Botão de configuração CORS

Dentro da área de texto, tenho algo como:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>https://*.example.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Em seguida, no painel Cloudfront ( https://console.aws.amazon.com/cloudfront/home ), criei uma distribuição e adicionei uma origem que apontava para o meu bucket S3 adicionando uma origem

Em seguida, foi adicionado um comportamento para que um caminho padrão aponte para a origem I baseada em S3. O que eu também fiz foi clicar nos cabeçalhos da lista de permissões e adicionar Origin: adicionando um comportamento e cabeçalhos de lista de permissões

O que acontece agora é o seguinte, que acredito estar certo:

1) Verifique se os cabeçalhos S3 estão sendo definidos corretamente

curl -i -H "Origin: https://example.com" https://s3.amazonaws.com/xxxxxxxxx/assets/fonts/my-cool-font.ttf
HTTP/1.1 200 OK
x-amz-id-2: Ay63Qb5uR98ag47SRJ91+YALtc4onRu1JUJgMTU98Es/pzQ3ckmuWhzzbTgDTCt+
x-amz-request-id: F1FFE275C0FBE500
Date: Thu, 14 Aug 2014 09:39:40 GMT
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000
Access-Control-Allow-Credentials: true
Vary: Origin, Access-Control-Request-Headers, Access-Control-Request-Method
Cache-Control: public, must-revalidate, proxy-revalidate, max-age=180
Last-Modified: Mon, 09 Dec 2013 14:29:04 GMT
ETag: "98918ee7f339c7534c34b9f5a448c3e2"
Accept-Ranges: bytes
Content-Type: application/x-font-ttf
Content-Length: 12156
Server: AmazonS3

2) Verifique se o Cloudfront funciona com os cabeçalhos

curl -i -H "Origin: https://example.com" https://xxxxx.cloudfront.net/assets/fonts/my-cool-font.ttf
HTTP/1.1 200 OK
Content-Type: application/x-font-ttf
Content-Length: 12156
Connection: keep-alive
Date: Thu, 14 Aug 2014 09:35:26 GMT
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000
Access-Control-Allow-Credentials: true
Cache-Control: public, must-revalidate, proxy-revalidate, max-age=180
Last-Modified: Mon, 09 Dec 2013 14:29:04 GMT
ETag: "98918ee7f339c7534c34b9f5a448c3e2"
Accept-Ranges: bytes
Server: AmazonS3
Vary: Origin
X-Cache: Miss from cloudfront
Via: 1.1 77bdacfea247b6cbe84dffa61da5a554.cloudfront.net (CloudFront)
X-Amz-Cf-Id: cmCxaUcFf3bT48zpPw0Q-vDDza0nZoWm9-_3qY5pJBhj64iTpkgMlg==

(Observe que o acima foi um erro do cloudfront porque esses arquivos são armazenados em cache por 180 segundos, mas o mesmo estava funcionando nas ocorrências)

3) Atinja o cloudfront com uma origem diferente (mas permitida no CORS para o bucket S3) - o Access-Control-Allow-Originnão é armazenado em cache! yay!

curl -i -H "Origin: https://www2.example.com" https://xxxxx.cloudfront.net/assets/fonts/my-cool-font.ttf
HTTP/1.1 200 OK
Content-Type: application/x-font-ttf
Content-Length: 12156
Connection: keep-alive
Date: Thu, 14 Aug 2014 10:02:33 GMT
Access-Control-Allow-Origin: https://www2.example.com
Access-Control-Allow-Methods: GET
Access-Control-Max-Age: 3000
Access-Control-Allow-Credentials: true
Cache-Control: public, must-revalidate, proxy-revalidate, max-age=180
Last-Modified: Mon, 09 Dec 2013 14:29:04 GMT
ETag: "98918ee7f339c7534c34b9f5a448c3e2"
Accept-Ranges: bytes
Server: AmazonS3
Vary: Origin
X-Cache: Miss from cloudfront
Via: 1.1 ba7014bad8e9bf2ed075d09443dcc4f1.cloudfront.net (CloudFront)
X-Amz-Cf-Id: vy-UccJ094cjdbdT0tcKuil22XYwWdIECdBZ_5hqoTjr0tNH80NQPg==

Observe acima que o domínio foi alterado com êxito sem um corte na cadeia de consulta.

Quando altero o cabeçalho Origin, parece sempre haver um X-Cache: Miss from cloudfrontpedido inicial e, depois, recebo o esperadoX-Cache: Hit from cloudfront

PS: Vale ressaltar que, ao fazer curl -I (maiúscula I), NÃO mostrará os cabeçalhos Access-Control-Allow-Origin como apenas uma HEAD, eu -i para torná-la GET e rolar para cima.

Eamonn Gahan
fonte
Trabalhou quando todos os outros não. Obrigado por reservar um tempo tão detalhado!
precisa
Funciona!! FYI - Eu tinha um enorme texto de resposta http ao testar isso ... vou editar a resposta para usar esta solução onda ... stackoverflow.com/questions/10060098/...
Michael Gorham
Legal obrigado pessoal - fico feliz em ver que está funcionando para os outros.
Eamonn Gahan
Não sei dizer o quanto você nos ajudou! 1
nada-especial-aqui
1
1 para a adição do cabeçalho de cliente Origina partir dos espectadores de modo que Cloudfront armazena o objecto com base nessa cabeçalho (e transmitir os cabeçalhos CORS servidor de volta para o utilizador)
Sebastien Saunier
13

Minhas fontes foram exibidas corretamente até o último envio para Heroku ... Não sei por que, mas o curinga na origem permitida do CORS parou de funcionar. Adicionei todos os meus domínios prepro e pro à política CORS na configuração de bucket, para que agora fique assim:

<CORSConfiguration>
    <CORSRule>
        <AllowedOrigin>http://prepro.examle.com</AllowedOrigin>
        <AllowedOrigin>https://prepro.examle.com</AllowedOrigin>
        <AllowedOrigin>http://examle.com</AllowedOrigin>
        <AllowedOrigin>https://examle.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Authorization</AllowedHeader>
    </CORSRule>

</CORSConfiguration>

UPDATE: adicione seu http://localhost:PORTtambém

luigi7up
fonte
1
Obrigado por compartilhar esta solução. Isso funcionou para mim.
Ryan Montgomery
8

Bem, a documentação afirma que você pode manter a configuração como "o sub-recurso cors no seu balde". Entendi que isso criaria um arquivo chamado "cors" na raiz do meu bucket com a configuração, mas isso não funcionaria. No final, tive que fazer login na área de administração do Amazon S3 e adicionar a configuração na propertiescaixa de diálogo do meu bucket.

O S3 poderia usar uma documentação melhor ...

nzifnab
fonte
1
Sim, mas tive a sorte de identificar algumas novas alterações na interface no painel de propriedades. Estive editando políticas de bucket, então naturalmente busco a configuração do CORS no mesmo painel.
VKen 1/10/12
trabalhou para mim, eu estava olhando para definir isso no meu aplicativo, que sabia que seria tão simples
Richlewis
7

Na configuração do Amazon S3 CORS (S3 Bucket / Permissions / CORS), se você usar isso:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>

O CORS funciona bem para arquivos Javascript e CSS, mas não funciona para arquivos de fonte .

Você deve especificar o domínio para permitir o CORS usando o padrão expresso na resposta @VKen: https://stackoverflow.com/a/25305915/618464

Então, use isto :

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
<CORSRule>
    <AllowedOrigin>https://*.mydomain.com</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

Lembre-se de substituir "mydomain.com" pelo seu domínio.

Depois disso, invalide o cache do CloudFront (CloudFront / Invalidações / Criar Invalidação) e funcionará.

educoutinho
fonte
6

No meu caso, eu não havia definido o namespace e a versão XML na configuração do CORS. Definindo aqueles trabalhados.

Alterado

<CORSConfiguration>

para

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
Gaurav Toshniwal
fonte
Funciona para mim também. Minhas fontes estão hospedadas no próprio bucket.
Khamaileon 31/07
Por que o modelo padrão não inclui automaticamente isso está além de mim.
precisa saber é o seguinte
4

Existe uma maneira melhor e mais fácil!

Pessoalmente, prefiro usar meus subdomínios DNS para resolver esse problema. Se o meu CDN estiver atrás do cdn.myawesomeapp.com em vez do sdf73n7ssa.cloudfront.net, os navegadores não vão surtar e bloqueá-los como problemas de segurança entre domínios.

Para apontar seu subdomínio para o domínio do AWS Cloudfront, acesse o painel de controle do AWS Cloudfront, selecione sua distribuição do Cloudfront e insira o subdomínio da CDN no campo Nomes de domínio alternativos (CNAMEs). Algo como cdn.myawesomeapp.com serve.

Agora você pode ir ao seu provedor de DNS (como AWS Route 53) e criar um CNAME para cdn.myawesomeapp.com apontando para sdf73n7ssa.cloudfront.net.

http://blog.cloud66.com/cross-origin-resource-sharing-cors-blocked-for-cloudfront-in-rails/

msroot
fonte
Isso quebra o SSL ou, pelo contrário, custa muito dinheiro com SSL, portanto, muitas pessoas não fazem isso.
24516 maletor
4

Essa configuração funcionou para mim. Eu posso listar objetos, recuperar, atualizar e excluir.

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <CORSRule>
    <AllowedOrigin>http://localhost:3000</AllowedOrigin>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
    <ExposeHeader>ETag</ExposeHeader>
    <ExposeHeader>x-amz-meta-custom-header</ExposeHeader>
  </CORSRule>
</CORSConfiguration>
Shahid
fonte
você precisa de domínio mudança, como eu estava testando a partir de localhost, basta olhar para esta página para CORS: docs.aws.amazon.com/AWSJavaScriptSDK/guide/...
Shahid
1
<ifModule mod_headers.c>

   Header set Access-Control-Allow-Origin: http://domainurl.com

</ifModule>

Solução Simples

O-mkar
fonte
Obrigado por compartilhar! Me deu a ideia de adicionar este cabeçalho como 'metadados' enquanto carregava ativos estáticos no armazenamento em nuvem. (Apesar de que maneira ele vai trabalhar com apenas 1 particular domainou all domains)
Vinay Vissh
0

Reiniciar meu aplicativo de inicialização da primavera (servidor) resolveu o problema para mim.

Eu havia configurado o CORS corretamente no S3. O curl estava dando a resposta correta com o cabeçalho de origem. O Safari estava buscando a fonte corretamente. Somente o cromo não estava disposto a aceitar o CORS.

Não tenho certeza do que exatamente causou o comportamento. Deve ter algo a ver com If-modified-since

Sujit Kamthe
fonte
0

Isso não está relacionado a fontes, mas a imagens, pode ser um caso delicado, mas, como aconteceu comigo, pode acontecer com outro. Vou deixar isso aqui esperando que ajude alguém:

Se você estiver no cenário "Eu fiz tudo o que eles disseram, mas ainda não funcionará", provavelmente é um problema relacionado ao cache no Chrome e no Safari. Vamos supor que seu servidor tenha um conjunto de configurações CORS adequado:

<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
    </CORSRule>
</CORSConfiguration>

e no Firefox tudo funciona bem, mas no Chrome e Safari não. Se você estiver acessando ao seu caminho de imagem remota de tanto uma simples <img src="http://my.remote.server.com/images/cat.png">tag e de um js elemento Imagem src, como a da seguinte forma:

var myImg = new Image()
myImg.crossOrigin = 'Anonymous'
myImg.onload = () => {
  // do stuff (maybe draw the downloaded img on a canvas)
}
myImg.src = 'http://my.remote.server.com/images/cat.png'

Você pode obter o No 'Access-Control-Allow-Origin'erro no Chrome e Safari. Isso acontece porque o primeiro <img>atrapalha de alguma maneira o cache do navegador e, quando você tenta acessar a mesma imagem posteriormente (no elemento Image no código), simplesmente quebra. Para evitar isso, você pode adicionar um parâmetro GET fictício a um caminho .src, para forçar o navegador a solicitar novamente a imagem e evitar o uso de cache, desta forma:

<img src="http://my.remote.server.com/images/cat.png?nocache=true"></img>
Nicola Elia
fonte
-1

Sim, claro. O Firefox suporta CORS para fontes, assim como as especificações exigem em http://dev.w3.org/csswg/css3-fonts/#allowing-cross-origin-font-loading

Boris Zbarsky
fonte
Obrigado pela sua pronta resposta, Boris Zbarsky. Você seria capaz de mostrar alguns exemplos de configurações para as configurações do S3 CORS?
VKen 3/09/12
Eu nunca pensei em configurar o S3 ... Quanto ao que enviar no nível HTTP, se você estiver bem, basta enviar "Access-Control-Allow-Origin: *" na resposta HTTP para os arquivos de fonte Deveria trabalhar.
Boris Zbarsky
Obrigado, estou tentando descobrir exatamente como fazer essa configuração com as configurações do S3 CORS.
VKen 6/09/12