CSS et CORS en local

Ce billet date de plusieurs années, ses informations peuvent être devenues obsolètes.

Je voulais tester CSS Shapes en local avec un code très basique :

img {
    float: left;
    shape-outside: url(star.png);
    shape-margin: 20px;
    margin: 0 20px 20px 0;
}

Mais ça n'a pas fonctionné tout de suite à cause d'un problème de CORS aux pieds (OH MON DIEU, j'ai honte de cette blague).

Le problème est le suivant : le mécanisme CORS des navigateurs n'autorise pas le protocole file:// et retourne null pour les fichiers locaux. Du coup mon shape-outside: url(star.png) ne fonctionne pas.

Ce comportement est connu. La solution proposée dans le fil des commentaires du problème est radicale :

Like I said, this sucks, but the only way to reliably do local dev is to start a local server. There are a lot of turn-key solutions for this.

Cela dit les restrictions d'accès avec CSS Shapes sont tout à fait compréhensible car le navigateur va jusqu'à analyser les pixels des images et ça pourrait poser des problèmes de sécurité.

Depuis Safari Technology Preview 29, ce comportement est aligné dans les trois plus gros navigateurs.

Mais pourquoi les navigateurs se comportent-ils de cette façon avec les fichiers locaux ? La sécurité des pages web locales est un sujet plus complexe qu'il n'y parait et la réponse semble se trouver dans la RFC 6454 qui définit le concept d'Origin :

If the origin is not a scheme/host/port triple, then return the string null

Si vous savez ce que vous faites, cette page de la documentation de three.js vous donne des pistes pour lancer un serveur local ou pour désactiver certaines options de sécurité dans les navigateurs.

CORS en quelques mots

Les navigateurs utilisent un modèle de sécurité basé sur l'origine. L'origine est le triplet protocole/nom de domaine/port de l'URI depuis laquelle la ressource est chargée.

Historiquement, Brendan Eich a posé les bases de la "Same Origin Policy" quand il a shippé JavaScript dans Netscape 2 en 1995 (en 10 jours blablabla…). Une petite pause wayback machine s'impose pour lire la release note lolesque de Netscape 2.0 (l'emphase est de moi) :

Netscape Navigator now includes a built-in scripting language, called JavaScript. JavaScript development is based on the JAVA programming language, which extends and enhances the capability of HTML documents.

Bref, plus tard le désarroi engendré par l'impossibilité d'XMLHttpRequester entre des domaines différents mènera aux palliatif à base de proxy ou de JSONP.

C'est sur ce terreau que CORS (Cross-Origin Resource Sharing) fut imaginé.

En quelques mots, le mécanisme CORS consiste en un jeu d'en-têtes HTTP. Il s'agit pour un client de s'identifier afin d'obtenir du serveur la permission de charger une ressource.

Le client ajoute un en-tête Origin à la requête HTTP pour s'identifier. C'est fait de manière automatique par le navigateur dans la plupart des cas.

Si le serveur décide d'accorder la permission de charger une ressource, il retourne une réponse contenant un en-tête HTTP Access-Control-Allow-Origin dont la valeur peut être soit la même origine que celle envoyée, soit *.

Pour des cas plus compliqués contenant des méthodes autres que GET ou POST, ou bien des en-têtes ou Content-Type spécifiques, le mécanisme CORS émet des preflight request que les amis de la latence connaissent bien.

Pour aller plus loin, je vous invite à lire ces papiers :

Avant Flexbox Après CSS Grid

Tag Kemar Joint