Le mécanisme CORS (Cross-origin resource sharing) permet de charger des ressources, notamment des assets, depuis un autre domaine que le domaine utilisé par une application web.
Par exemple mon application est sur app.com, je veux charger une image depuis assets.com.
L’intérêt est d’ouvrir son application à l’utilisation de CDN et de servir ses fichiers statiques depuis plusieurs endroits et au plus prêt de l’utilisateur final.
En effet si je dois charger une image depuis mon application, un CDN comme CloudFront me la renverra probablement depuis un endroit différent selon que j’utilise l’application depuis l’Europe ou les états Unis.
D’autre part, chaque navigateur peut ouvrir un nombre limité de connexions HTTP vers un même domaine. Les dernières versions des navigateurs sont généralement autour de 6/8.
Cela signifie que si vous chargez 10 fichiers depuis un même domaine et que votre navigateur peut ouvrir 8 connexions, il devra attendre d’avoir chargé le 1er fichier pour charger le 9ème.
Ce «problème» peut facilement être contourné en faisant pointer des sous domaines de son application vers son CDN. C’est généralement ce qu’on fait avec son sous domaine CloudFront.
C’est à dire faire pointer cdn{1..4}.app.com vers assets.com au niveau DNS.
Le support de CORS par les navigateurs est plus que bon puisque toutes les dernières versions des principaux navigateurs le supportent.
Par contre les mécanismes de sécurité mis en place d’un navigateur à l’autre ne sont pas les même.
En effet, la possibilité d’ouvrir une ressource depuis un autre domaine peut poser quelque soucis de sécurité, puisque par définition on charge un contenu sur lequel on n’a pas de contrôle.
Par exemple les requêtes Ajax était traditionnellement limitées à une police de sécurité stricte des navigateurs, les cantonnant au même domaine. Ce n’est plus le cas depuis l’avènement d’HTML5.
Pour éviter que tout le monde puisse charger n’importe quel contenu, le mécanisme CORS a été mis en place.
Il prévoit que la source du contenu chargé doit explicitement spécifier les domaines qu’elle accepte pour accéder à ses ressources, via l’entête Access-Control-Allow-Origin
.
En tant que domaine assets.com, je peux donc annoncer que seul de domaine app.com peut accéder aux ressources proposées.
Ces mécanismes de protection s’appliquent aux JavaScript mais également aux webfont dans le cas de Firefox. Autant Safari et Chrome ne broncheront pas si vous essayez de charger une font type .woff depuis un CDN, autant Firefox s’attend à recevoir les entêtes précisant que le domaine de destination nous y autorise.
Cela signifie que lorsqu’on utilise un CDN comme CloudFront on doit renvoyer ces entêtes sinon la font en question ne sera pas chargée.
Ce n’est peut être pas très grave dans le cas d’une font de caractères ou vous avez sûrement un fallback vers une font classique ; mais dans le cas d’une font d’images c’est plus gênant, voyez plutôt:
Le fonctionnement de CloudFront est simple. Imaginons que vous l’utilisiez pour vos images.
<img src="//app.CloudFront.net/images/awesome.png" alt="Awesome image" />
Si l’image est présente sur votre CDN il la sert directement, si ce n’est pas le cas il la demande à votre application et la met en cache pour la servir directement la fois prochaine.
C’est donc bien au niveau du serveur web de votre application qu’il va falloir renvoyer les entêtes adaptés.
En l’occurrence dans Nginx:
location ~* \.(eot|ttf|woff)$ { add_header "Access-Control-Allow-Origin" "app.com"; }
Pour que CloudFront ait «connaissance» de votre application il faut lui donner l’URL de celle via l’onglet Origin.
Lorsque vous faites une requête sur CloudFront, celui ci renvoie les même entêtes que si c’était votre application qui répondait.
Dès lors qu’on consultera un .woff on enverra donc l’entête Access-Control-Allow-Origin
Côté CloudFront il faut s’assurer qu’il laisse passer l’origine. Rendez vous sur votre console AWS, CloudFront, choisissez Distribution Settings, Onglet Behaviors.
Nous allons maintenant indiquer que nous souhaitons transférer certains entête de la requête initiale, notamment l’origine.
En effet pour simplifier la mise en cache CloudFront ne retient par défaut aucun entête.
C’est tout pour la mise en place de CORS avec CloudFront
Regardons les headers retournés par Google fonts lorsqu’on charge une font:
wget --server-response -O /dev/null http://themes.googleusercontent.com/static/fonts/opensans/v8/cJZKeOuBrn4kERxqtaUH3bO3LdcAZYWl9Si6vvxL-qU.woff
HTTP request sent, awaiting response... HTTP/1.1 200 OK Content-Type: font/woff Last-Modified: Tue, 28 Jan 2014 23:51:09 GMT Date: Wed, 02 Jul 2014 14:25:43 GMT Expires: Thu, 02 Jul 2015 14:25:43 GMT Access-Control-Allow-Origin: * Timing-Allow-Origin: * X-Content-Type-Options: nosniff Server: sffe Content-Length: 14604 X-XSS-Protection: 1; mode=block Cache-Control: public, max-age=31536000 Age: 416552 Alternate-Protocol: 80:quic Length: 14604 (14K) [font/woff] Saving to: '/dev/null'
Google fonts agit ici comme un CDN, l’avantage est que la font a pu être potentiellement déjà chargée par une autre application, et par conséquent peut déjà se retrouver dans le cache du navigateur client.
Ici l’entête qui nous intéresse le plus est le header Access-Control-Allow-Origin: *
,
qui indique que le contenu peut être chargé depuis n’importe quel origine. On peut donc parfaitement
utiliser un wildcard pour cet entête.
On notera que la mise en cache est particulièrement longue (1 an), puisque ce contenu n’a pas vocation à changer régulièrement.
CORS ne se limite pas au verbe HTTP GET, en effet la spec prévoit qu’il est possible de faire du CORS en POST par exemple. Dans ce cas le navigateur doit envoyer une requête avant (preflight) afin de demander quelles méthodes il peut utiliser pour cette ressource.
J’espère que cet article vous permettra d’y voir plus clair sur l’intérêt et le fonctionnement de CORS.
L’équipe Synbioz.
Libres d’être ensemble.
Nos conseils et ressources pour vos développements produit.