Chargement des polices entre domaines Amazon S3 CORS (Cross-Origin Resource Sharing) et Firefox

135

Il y a eu un problème de longue date avec Firefox ne chargeant pas la police d'origine différente de la page Web actuelle. Habituellement, le problème survient lorsque les polices sont servies sur des CDN.

Diverses solutions ont été soulevées dans d'autres questions:

CSS @ font-face ne fonctionne pas avec Firefox, mais fonctionne avec Chrome et IE

Avec l'introduction d'Amazon S3 CORS, existe-t-il une solution utilisant CORS pour résoudre le problème de chargement des polices dans Firefox?

edit: Ce serait formidable de voir un exemple de la configuration S3 CORS.

edit2: J'ai trouvé une solution de travail sans vraiment comprendre ce qu'elle a fait. Si quelqu'un pouvait fournir des explications plus détaillées sur les configurations et la magie d'arrière-plan qui se produit sur l'interprétation d'Amazon de la configuration, cela sera grandement apprécié, comme avec nzifnab qui a mis en place une prime pour cela.

VKen
la source

Réponses:

148

Mise à jour du 10 septembre 2014:

Vous ne devriez plus avoir à effectuer les hacks de chaîne de requête ci-dessous car Cloudfront prend désormais correctement en charge CORS. Voir http://aws.amazon.com/blogs/aws/enhanced-cloudfront-customization/ et cette réponse pour plus d'informations: https://stackoverflow.com/a/25305915/308315


OK, j'ai enfin fait fonctionner les polices en utilisant la configuration ci-dessous avec un petit ajustement des exemples dans la documentation.

Mes polices sont hébergées sur S3, mais mises en avant par cloudfront.

Je ne sais pas pourquoi cela fonctionne, je suppose que le <AllowedMethod> GETet <AllowedHeader> Content-*est nécessaire.

Si quelqu'un maîtrisant la configuration d'Amazon S3 CORS peut éclairer cela, cela sera grandement apprécié.

<?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>

Éditer:

Certains développeurs sont confrontés à des problèmes de mise en cache de l'en- Access-Control-Allow-Origintête par Cloudfront . Ce problème a été résolu par le personnel AWS dans le lien ( https://forums.aws.amazon.com/thread.jspa?threadID=114646 ) ci-dessous, commenté par @ Jeff-Atwood.

À partir du thread lié, il est conseillé, comme solution de contournement, d'utiliser une chaîne de requête pour différencier les appels de différents domaines. Je vais reproduire l'exemple abrégé ici.

Utilisation curlpour vérifier les en-têtes de réponse:

Domaine A: a.domain.com

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

En-têtes de réponse du domaine 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

Domaine B: b.domain.com

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

En-têtes de réponse du domaine 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

Vous remarquerez que le Access-Control-Allow-Origina renvoyé des valeurs différentes, qui ont dépassé la mise en cache Cloudfront.

VKen
la source
2
Avez-vous rencontré des problèmes similaires à ceux décrits ici - l'en- Access-Control-Allow-Origintête est mis en cache et invalide CORS lorsqu'une demande ultérieure est effectuée via un sous-domaine différent?
ov
1
@ov Je ne rencontre pas le problème car j'ai défini explicitement les domaines qui utilisent les ressources. J'ai lu le lien que vous avez publié auparavant. Je me suis vaguement souvenu de certaines réponses sur un autre fil disant que les domaines doivent être explicitement indiqués, donc <AllowedOrigin> * </AllowedOrigin> n'est pas réellement autorisé, en raison de certaines restrictions. Je ne trouve pas ces messages de réponse maintenant, cela pourrait être un article de blog que j'ai lu ailleurs. J'espère que cela pourra aider.
VKen
3
Vous pouvez avoir plusieurs éléments AllowedOrigin dans un seul élément CORSRule, vous pouvez donc combiner ces CORSRules en un seul élément, car les autres éléments qu'ils contiennent sont identiques.
Ben Hull
4
@dan si le compartiment S3 est servi par CloudFront, il semble que la réponse consiste à faire varier la chaîne de requête de police par domaine, comme indiqué dans cette réponse officielle d'Amazon: forums.aws.amazon.com/thread.jspa?threadID=114646
Jeff Atwood
2
Cela a été un problème extrêmement frustrant. La bonne nouvelle est que S3 semble maintenant faire la bonne chose, il est donc au moins possible de servir tout autre chose que les polices Web via CloudFront et de servir les fichiers de polices directement à partir de S3. Malheureusement, le hack de chaîne de requête n'est pas vraiment pratique dans notre application sans un refactoring plus important, car les actifs sont tous servis via le pipeline d'actifs Rails, et il n'y a pas de moyen pratique de modifier les URL d'actifs au moment de la demande (ils sont tous générés lors du déploiement. lorsque les actifs sont précompilés). L'URL de la police en css est déjà active sur S3.
Zach Lipton
97

Après quelques ajustements, il semble que cela fonctionne sans le piratage de la chaîne de requête. Plus d'informations ici: http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorS3Origin.html#RequestS3-cors

Je vais passer en revue toute ma configuration pour qu'il soit facile de voir ce que j'ai fait, j'espère que cela aidera les autres.

Informations générales: J'utilise une application Rails qui a le gem asset_sync pour placer des actifs sur S3. Cela inclut les polices.

Dans la console S3, j'ai cliqué sur mon compartiment, mes propriétés et `` modifier la configuration des cors '', ici: Bouton de configuration CORS

À l'intérieur de la zone de texte, j'ai quelque chose comme:

<?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>

Ensuite, dans le panneau Cloudfront ( https://console.aws.amazon.com/cloudfront/home ), j'ai créé une distribution, ajouté une origine qui pointait vers mon compartiment S3 ajouter une origine

Puis ajouté un comportement pour qu'un chemin par défaut pointe vers la configuration d'origine basée sur S3 I. J'ai également cliqué sur les en-têtes de la liste blanche et ajouté Origin: ajout d'un comportement et d'en-têtes de liste blanche

Ce qui se passe maintenant est le suivant, ce qui, à mon avis, est juste:

1) Vérifiez que les en-têtes S3 sont correctement définis

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) Vérifiez que Cloudfront fonctionne avec les en-têtes

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==

(Notez que ce qui précède était un échec de cloudfront car ces fichiers sont mis en cache pendant 180 secondes, mais la même chose fonctionnait sur les hits)

3) Hit cloudfront avec une origine différente (mais une qui est autorisée sur CORS pour le compartiment S3) - le Access-Control-Allow-Originn'est pas mis en 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==

Notez ci-dessus que le domaine a été modifié avec succès sans piratage de chaîne de requête.

Quand je change l'en-tête Origin, il semble toujours y avoir un X-Cache: Miss from cloudfrontsur la première demande puis après j'obtiens leX-Cache: Hit from cloudfront

PS Il est intéressant de noter que lorsque vous faites curl -I (majuscule I) ne montrera PAS les en-têtes Access-Control-Allow-Origin car il ne s'agit que d'une HEAD, je fais -i pour en faire un GET et faire défiler vers le haut.

Eamonn Gahan
la source
A travaillé quand tous les autres ne l'ont pas fait. Merci d'avoir pris le temps de poster avec autant de détails!
iwasrobbed le
Ça marche!! FYI - J'ai eu un énorme texte de réponse http lors du test ... je vais éditer la réponse pour utiliser cette solution curl ... stackoverflow.com/questions/10060098
Michael Gorham
Cool merci les gars - heureux de voir que cela fonctionne pour les autres.
Eamonn Gahan
Je ne peux pas vous dire à quel point vous nous avez aidés! +1
rien de spécial-ici
1
+1 pour ajouter l'en-tête client Origindes téléspectateurs afin que Cloudfront mette en cache l'objet en fonction de cet en-tête (et renvoie les en-têtes CORS du serveur à l'utilisateur)
Sébastien Saunier
13

Mes polices ont été servies correctement jusqu'à la dernière poussée vers Heroku ... Je ne sais pas pourquoi, mais le joker dans l'origine autorisée CORS a cessé de fonctionner. J'ai ajouté tous mes domaines prépro et pro à la stratégie CORS dans le paramètre de compartiment, donc maintenant, cela ressemble à ceci:

<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>

MISE À JOUR: ajoutez le vôtre http://localhost:PORTaussi

luigi7up
la source
1
Merci de partager cette solution. Cela a fonctionné pour moi.
Ryan Montgomery
8

Eh bien, la documentation indique que vous pouvez coller la configuration en tant que «sous-ressource cors dans votre compartiment». J'ai pris cela pour signifier que je créerais un fichier appelé "cors" à la racine de mon seau avec la configuration, mais cela ne fonctionnerait pas. En fin de compte, j'ai dû me connecter à la zone d'administration Amazon S3 et ajouter la configuration dans la propertiesboîte de dialogue de mon compartiment.

S3 pourrait utiliser une meilleure documentation ...

nzifnab
la source
1
Oui, mais j'ai eu la chance de repérer de nouveaux changements d'interface dans le panneau des propriétés. J'ai édité des politiques de seau, donc naturellement je recherche la configuration CORS dans le même panneau.
VKen
travaillé pour moi, je cherchais à définir cela dans mon application, qui savait que ce serait si simple
Richlewis
7

Dans la configuration Amazon S3 CORS (S3 Bucket / Permissions / CORS) si vous utilisez ceci:

<?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>

CORS fonctionne bien pour les fichiers Javascript et CSS, mais il ne fonctionne pas pour les fichiers de polices .

Vous devez spécifier le domaine pour autoriser CORS en utilisant le modèle exprimé dans la réponse @VKen: https://stackoverflow.com/a/25305915/618464

Alors, utilisez ceci :

<?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>

N'oubliez pas de remplacer "mydomain.com" pour votre domaine.

Après cela, invalidez le cache CloudFront (CloudFront / Invalidations / Create Invalidation) et cela fonctionnera.

educoutinho
la source
6

Dans mon cas, je n'avais pas défini d'espace de noms et de version XML dans la configuration CORS. Définir ceux qui ont fonctionné.

Modifié

<CORSConfiguration>

à

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
Gaurav Toshniwal
la source
Fonctionne aussi pour moi. Mes polices sont hébergées sur le bucket lui-même.
khamaileon le
Pourquoi le modèle par défaut n'inclut pas automatiquement cela me dépasse.
CoatedMoose
4

Il existe un moyen meilleur et plus simple!

Personnellement, je préfère utiliser mes sous-domaines DNS pour résoudre ce problème. Si mon CDN est derrière cdn.myawesomeapp.com au lieu de sdf73n7ssa.cloudfront.net, les navigateurs ne vont pas paniquer et les bloquer en tant que problèmes de sécurité interdomaines.

Pour faire pointer votre sous-domaine vers votre domaine AWS Cloudfront, accédez au panneau de configuration AWS Cloudfront, sélectionnez votre distribution Cloudfront et entrez votre sous-domaine CDN dans le champ Noms de domaine alternatifs (CNAME). Quelque chose comme cdn.myawesomeapp.com fera l'affaire.

Vous pouvez maintenant accéder à votre fournisseur DNS (comme AWS Route 53) et créer un CNAME pour cdn.myawesomeapp.com pointant vers sdf73n7ssa.cloudfront.net.

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

msroot
la source
Cela brise SSL ou plutôt cela coûte beaucoup d'argent à faire avec SSL, donc beaucoup de gens ne le font pas.
maletor
4

Cette configuration a fonctionné pour moi. Je peux lister un objet, récupérer, mettre à jour et supprimer.

<?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
la source
vous devez changer de domaine, car je testais
Shahid
1
<ifModule mod_headers.c>

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

</ifModule>

Solution simple

O-mkar
la source
Merci d'avoir partagé! M'a donné l'idée d'ajouter simplement cet en-tête en tant que `` méta-données '' lors du téléchargement d'actifs statiques sur le stockage cloud. (Bien que de cette façon, cela fonctionnera avec seulement 1 particular domainou all domains)
Vinay Vissh
0

Le redémarrage de mon application Spring Boot (serveur) a résolu le problème pour moi.

J'avais configuré CORS correctement sur S3. La boucle donnait la bonne réponse avec l'en-tête d'origine. Safari récupérait la police correctement. Ce n'était que le chrome qui n'était pas disposé à accepter le CORS.

Je ne sais pas exactement ce qui a causé le comportement. Doit avoir quelque chose à voir avec If-modified-since

Sujit Kamthe
la source
0

Ce n'est pas lié aux polices mais aux images, cela peut être un cas limite, mais comme cela m'est arrivé, cela pourrait arriver à un autre. Je vais laisser ceci ici en espérant que cela aidera quelqu'un:

Si vous êtes dans le scénario "J'ai fait tout ce qu'ils ont dit, mais cela ne fonctionnera toujours pas", c'est probablement un problème lié au cache dans Chrome et Safari. Supposons que votre serveur dispose d'un jeu de configuration CORS approprié:

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

et dans Firefox, tout fonctionne bien, mais pas dans Chrome et Safari. Si vous accédez au chemin de votre image distante à la fois à partir d' une <img src="http://my.remote.server.com/images/cat.png">balise simple et d'un élément js Image src, comme de la manière suivante:

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'

Vous pourriez obtenir l' No 'Access-Control-Allow-Origin'erreur dans Chrome et Safari. Cela se produit parce que le premier <img>gâche d'une manière ou d'une autre le cache du navigateur, et lorsque vous essayez d'accéder à la même image plus tard (sur l'élément Image dans le code), il se brise simplement. Pour éviter cela, vous pouvez ajouter un paramètre GET fictif à un chemin .src, afin de forcer le navigateur à redemander l'image et éviter d'utiliser le cache, comme ceci:

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

Oui bien sûr. Firefox prend en charge CORS pour les polices, tout comme la spécification l'exige à http://dev.w3.org/csswg/css3-fonts/#allowing-cross-origin-font-loading

Boris Zbarsky
la source
Merci pour votre réponse rapide, Boris Zbarsky. Pourriez-vous montrer quelques exemples de configuration pour les paramètres S3 CORS?
VKen
Je n'ai jamais envisagé de configurer S3 ... En ce qui concerne ce qu'il faut envoyer au niveau HTTP, si vous êtes d'accord, envoyez simplement "Access-Control-Allow-Origin: *" dans la réponse HTTP pour les fichiers de police devrait marcher.
Boris Zbarsky
Merci, j'essaie de savoir exactement comment faire ce paramètre avec les configurations S3 CORS.
VKen