# HSTS contre upgrade-insecure-requests, faut-il les deux (/fr/blog/hsts-vs-upgrade-insecure-requests)



HSTS force chaque navigation vers votre hôte à passer en HTTPS. La directive CSP
`upgrade-insecure-requests` réécrit les URL de sous-ressources non sécurisées à
l'intérieur de la page, tiers compris. Ils sont complémentaires, pas des
alternatives, et un site qui ne veut aucune requête non sécurisée envoie les deux.

On traite souvent ces deux mécanismes comme deux façons de faire la même chose,
forcer HTTPS. Ce n'est pas le cas.
[HTTP Strict Transport Security (HSTS)](/fr/docs/web-security/security-headers/strict-transport-security)
contrôle **comment le navigateur joint votre hôte**. Il fait passer chaque navigation
vers votre domaine en HTTPS, y compris un clic depuis un lien externe, une fois que le
navigateur connaît l'hôte. La directive CSP
[`upgrade-insecure-requests`](/fr/docs/web-security/policies/content-security-policy/directives/upgrade-insecure-requests)
contrôle **les URL que vos pages chargent une fois ouvertes.** Elle réécrit les
requêtes de sous-ressources non sécurisées à l'intérieur de votre document, y compris
vers des hôtes tiers que HSTS ne peut pas atteindre. Ils ne se recouvrent presque nulle
part, et c'est pourquoi vous envoyez les deux.

## HSTS vs upgrade-insecure-requests, les deux moitiés du HTTPS forcé [#hsts-vs-upgrade-insecure-requests-les-deux-moitiés-du-https-forcé]

Pensez à deux questions distinctes. Premièrement, quand un navigateur va sur votre site,
se connecte-t-il en HTTPS ou commence-t-il par un saut en clair qu'un attaquant peut
retirer ? C'est une question de transport, sur le fait de joindre votre hôte.
Deuxièmement, une fois votre page chargée, les ressources qu'elle tire, les vôtres et
celles des tiers, se chargent-elles toutes en HTTPS, ou une image `http://` égarée
déclenche-t-elle du mixed content ? C'est une question de contenu, sur ce qui s'exécute
à l'intérieur de la page.

HSTS possède la première question. `upgrade-insecure-requests` possède la seconde. Ni
l'un ni l'autre ne répond à celle de l'autre.

## HSTS sécurise comment le navigateur joint votre hôte [#hsts-sécurise-comment-le-navigateur-joint-votre-hôte]

HSTS est un header de réponse, défini par le [RFC 6797](https://datatracker.ietf.org/doc/html/rfc6797),
qu'un site envoie en HTTPS pour dire au navigateur « pendant les N prochaines secondes,
ne parle à cet hôte qu'en HTTPS ». Le navigateur met votre domaine en cache comme hôte
HSTS connu, réécrit toute URL `http://` le concernant en `https://` avant que la requête
ne quitte la machine, et échoue durement sur les erreurs de certificat sans possibilité
de passer outre.

```http
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
```

`max-age` est la durée de vie requise en secondes, et chaque réponse HTTPS qui porte
le header remet le compteur à zéro, donc les visiteurs réguliers ne sortent jamais de
la politique. Envoyer `max-age=0` en HTTPS supprime l'entrée en cache, c'est la porte
de sortie si vous avez posé le header par erreur. Le navigateur ignore complètement le
header quand il arrive en HTTP simple ; sinon, un attaquant assis sur le saut non
sécurisé pourrait poser ou effacer votre politique à votre place. HSTS ne fonctionne
aussi que pour les noms de domaine, une adresse IP ne peut pas être un hôte HSTS connu.

`includeSubDomains` étend la politique à chaque sous-domaine, vers le bas depuis l'hôte
qui l'a envoyée. La justification de l'OWASP mérite d'être gardée en tête : un cookie
de session scopé au domaine parent peut être manipulé depuis un sous-domaine non
sécurisé, donc couvrir tout l'arbre est ce qui protège réellement la session.

Ce que cela stoppe, c'est le protocol downgrade, aussi appelé SSL stripping : un
attaquant sur le réseau qui transforme votre connexion HTTPS en une connexion en clair,
ou usurpe un certificat. Comme le navigateur passe en HTTPS avant que la requête ne
parte et refuse de laisser l'utilisateur ignorer une erreur TLS, il n'y a aucun saut
non sécurisé à retirer. La seule faille est la toute première requête vers un hôte que
le navigateur n'a jamais vu, le RFC 6797 l'appelle la vulnérabilité MITM de bootstrap,
et le preload la referme.

La limite de portée clé : HSTS ne couvre que les hôtes que vous contrôlez et qui
envoient le header. Il n'a rien à dire sur un domaine tiers depuis lequel votre page
charge un script.

### Le preload, et pourquoi on y monte par étapes [#le-preload-et-pourquoi-on-y-monte-par-étapes]

`preload` est le signal d'opt-in pour la liste de preload des navigateurs sur
[hstspreload.org](https://hstspreload.org/), qui embarque la politique dans le
navigateur lui-même pour qu'une toute première visite soit elle aussi protégée. Il ne
fait pas partie du RFC 6797 ; c'est un mécanisme de facto géré via la liste de Chrome
que les autres navigateurs consomment. La soumission exige un certificat valide, une
redirection HTTP vers HTTPS sur le même hôte, chaque sous-domaine servi en HTTPS, un
`max-age` d'au moins un an, à la fois `includeSubDomains` et `preload` dans le header,
et le header présent sur la redirection HTTPS elle-même.

Ces exigences sont aussi le piège. Preload plus `includeSubDomains` force HTTPS sur
chaque sous-domaine, y compris les internes ou les oubliés, pour chaque utilisateur de
chaque navigateur qui embarque la liste. Revenir en arrière est lent : vous retirez la
directive `preload`, demandez le retrait, puis attendez, car le retrait met 6 à 12
semaines à atteindre la plupart des utilisateurs de Chrome et plus longtemps pour les
autres navigateurs, parce que la liste est codée en dur dans les versions des
navigateurs. Montez donc par étapes, comme hstspreload.org le prescrit lui-même :
`max-age=300`, puis `604800` (une semaine), puis `2592000` (un mois), en laissant
s'écouler chaque `max-age` avant de l'augmenter, et seulement ensuite engagez-vous sur
la valeur d'un an ou plus avec `preload`.

## upgrade-insecure-requests corrige le mixed content dans vos pages [#upgrade-insecure-requests-corrige-le-mixed-content-dans-vos-pages]

`upgrade-insecure-requests` est une directive de
[politique de sécurité du contenu (CSP)](/fr/docs/web-security/policies/content-security-policy).
Quand une page la porte, le navigateur réécrit chaque URL de sous-ressource `http://`
non sécurisée de ce document en `https://` avant que la requête ne soit faite,
first-party comme tiers : images, scripts, styles, frames, fetch. Les navigations sont
traitées plus étroitement. Le navigateur passe en HTTPS les navigations vers votre
propre hôte, les soumissions de formulaires et les frames imbriquées, mais laisse
tranquilles les liens vers des sites tiers ; les auteurs de la spec ont jugé que
réécrire l'URL de navigation de quelqu'un d'autre porte un risque de casse trop élevé,
donc un simple lien vers `http://other.example/` est demandé tel quel.

```http
Content-Security-Policy: upgrade-insecure-requests
```

Pour les sous-ressources, la portée est l'opposé de HSTS. Elle s'applique à **chaque**
URL que votre page charge, y compris des hôtes tiers que vous ne possédez pas et pour
lesquels vous ne pourriez jamais envoyer HSTS. Si votre page référence
`http://widgets.example.net/w.js`, HSTS sur votre propre domaine n'y fait rien, mais
`upgrade-insecure-requests` la réécrit en `https://` avant que la requête ne soit
faite. C'est ainsi que vous éliminez les avertissements de mixed content sur une page
qui tire de nombreuses origines sans éditer chaque URL à la main.

La réécriture est aveugle, et il n'y a pas de repli. Si une ressource n'est pas
réellement disponible en HTTPS, la requête passée en HTTPS échoue et le navigateur ne
réessaie jamais en HTTP. Le prérequis de déploiement est que tout ce que vos pages
chargent soit servi à la même URL sur les deux schémas.

Il est aussi utile de connaître la base de départ que vous améliorez. Les navigateurs
modernes passent déjà automatiquement en HTTPS ce que la spec Mixed Content appelle le
contenu upgradable : les images, l'audio et la vidéo chargés via `src`, plus les images
CSS. Tout le reste du contenu non sécurisé, scripts, feuilles de style, iframes, appels
fetch et XHR, polices, est bloqué net plutôt que chargé. Donc sans la directive, un
script `http://` n'est pas un trou silencieux, c'est une page cassée. Ce que
`upgrade-insecure-requests` ajoute, c'est que ce contenu blocable est passé en HTTPS
au lieu d'être bloqué, et la console arrête de se remplir d'avertissements de mixed
content. Un cas limite survit dans les deux situations : le mixed content adressé par
une adresse IP est bloqué, pas passé en HTTPS. La directive sœur dépréciée,
[`block-all-mixed-content`](/fr/docs/web-security/policies/content-security-policy/directives/block-all-mixed-content),
prenait l'approche inverse et bloquait ce contenu au lieu de le passer en HTTPS.

## Pourquoi aucun ne remplace l'autre [#pourquoi-aucun-ne-remplace-lautre]

Ils résolvent des problèmes différents, donc l'un ne peut pas se substituer à l'autre,
et les specs le disent elles-mêmes. MDN énonce que `upgrade-insecure-requests` « ne
remplace pas le header `Strict-Transport-Security` (HSTS) », et la spec du W3C est
encore plus directe : la directive « ne déprécie pas, ne remplace pas et ne réduit en
aucune façon la valeur de » HSTS. Passer en HTTPS les URL à l'intérieur d'une page ne
fait rien pour la navigation de premier niveau qui a chargé la page en premier lieu. Un
utilisateur qui clique un lien `http://` vers votre site depuis ailleurs a besoin de
HSTS pour être passé en HTTPS avant que la connexion ne soit faite ; la directive CSP
ne s'exécute jamais, parce que la page n'est pas encore chargée.

Prenez-le dans l'autre sens et la faille est tout aussi nette. HSTS passe en HTTPS les
requêtes vers votre propre hôte, donc une référence `http://` égarée vers votre propre
domaine est corrigée, mais une sous-ressource tierce `http://` est entièrement hors de
sa portée. Seul `upgrade-insecure-requests` l'atteint.

## Ce que chacun couvre [#ce-que-chacun-couvre]

|                                                     | Strict-Transport-Security (HSTS)                          | upgrade-insecure-requests                            |
| --------------------------------------------------- | --------------------------------------------------------- | ---------------------------------------------------- |
| Couche                                              | Transport, comment le navigateur joint votre hôte         | Contenu, URL chargées dans votre page                |
| Portée                                              | Votre hôte et les sous-domaines que vous couvrez          | Chaque sous-ressource, hôtes tiers inclus            |
| Navigation de premier niveau depuis un lien externe | Passée en HTTPS, une fois connu ou preloadé               | Non couverte                                         |
| Mixed content tiers                                 | Non couvert                                               | Passé en HTTPS                                       |
| Menace                                              | SSL stripping, protocol downgrade                         | Mixed content sur la page                            |
| En cas d'échec                                      | Erreurs de certificat fatales, impossible de passer outre | La requête passée en HTTPS échoue, pas de repli HTTP |
| Où il est posé                                      | Header de réponse `Strict-Transport-Security`             | Directive CSP                                        |
| Support                                             | Tous les moteurs modernes (Blink, Gecko, WebKit)          | Tous les moteurs modernes (Blink, Gecko, WebKit)     |

## Recommandation [#recommandation]

Envoyez les deux. Posez HSTS avec un long `max-age` et `includeSubDomains`, montez le
`max-age` par étapes, et n'ajoutez `preload` qu'une fois chaque sous-domaine confirmé
en HTTPS, pour que les navigations vers votre hôte soient verrouillées. Ajoutez
`upgrade-insecure-requests` à votre politique de sécurité du contenu pour que chaque
sous-ressource, y compris les tiers que HSTS ne peut pas atteindre, se charge en HTTPS,
après avoir vérifié que ces ressources existent réellement en HTTPS, car un passage en
HTTPS qui échoue ne retombe pas en HTTP. Ensemble, ils couvrent tout le trajet : HSTS
amène le navigateur à votre hôte en sécurité, et la directive CSP garde en HTTPS tout
ce que la page charge ensuite.

Pour confirmer que les deux sont présents et bien formés sur tout votre site, lancez le
[scanner de headers de sécurité](/tools/security-headers) gratuit de CentralCSP. Il
signale un header HSTS manquant ou faible et montre si votre politique porte
`upgrade-insecure-requests`.

La partie upgrade mérite aussi d'être surveillée après le scan. Une requête upgradée qui
n'a pas de version HTTPS échoue purement et simplement, et rien dans la page ne vous dit
quel tiers a cassé. Comme l'échec est une erreur réseau, un endpoint CentralCSP qui reçoit
les [reports NEL](/fr/docs/web-security/reporting-api/reports/network-error) fait remonter
ces hosts depuis le trafic réel, ce qui vous permet de trouver la ressource qui n'existe
qu'en HTTP avant vos visiteurs.

## FAQ [#faq]

### upgrade-insecure-requests remplace-t-il HSTS ? [#upgrade-insecure-requests-remplace-t-il-hsts-]

Non. La directive ne réécrit que les URL à l'intérieur d'une page déjà chargée, donc
elle ne peut pas passer en HTTPS la navigation qui amène un utilisateur sur votre site
depuis un lien `http://` ailleurs. MDN comme la spec du W3C disent de garder HSTS posé
à côté ; la formulation de la spec est que la directive « ne déprécie pas, ne remplace
pas et ne réduit en aucune façon la valeur de » HSTS.

### Faut-il à la fois HSTS et upgrade-insecure-requests ? [#faut-il-à-la-fois-hsts-et-upgrade-insecure-requests-]

Oui, ils couvrent deux moitiés différentes. HSTS fige la façon dont les navigateurs
joignent votre hôte d'une visite à l'autre, et dès la première visite une fois
preloadé, ce que la directive CSP ne pourra jamais faire. `upgrade-insecure-requests`
passe en HTTPS les sous-ressources dans la page, y compris les hôtes tiers que votre
header HSTS ne peut pas couvrir.

### Comment upgrade-insecure-requests corrige-t-il le mixed content ? [#comment-upgrade-insecure-requests-corrige-t-il-le-mixed-content-]

Il dit au navigateur de réécrire chaque URL de sous-ressource `http://` non sécurisée
de la page, first-party comme tierce, en `https://` avant que la requête ne soit faite,
pour que les images, scripts, styles et frames se chargent de façon sécurisée au lieu
d'être bloqués ou signalés comme mixed content. Il n'y a pas de repli : une ressource
non disponible en HTTPS échoue, tout simplement.

### Le preload HSTS est-il sans risque ? [#le-preload-hsts-est-il-sans-risque-]

Il est sans risque une fois chaque sous-domaine confirmé en HTTPS, mais il est presque
irréversible à échelle humaine. Le preload exige un `max-age` minimum d'un an avec
`includeSubDomains`, donc chaque sous-domaine, y compris les internes que vous avez
oubliés, est forcé en HTTPS pour tous les utilisateurs. Le retrait de la liste met 6 à
12 semaines à atteindre la plupart des utilisateurs de Chrome, plus longtemps pour les
autres navigateurs. Montez le `max-age` par étapes (300, puis 604800, puis 2592000)
avant de vous engager.

## Sources [#sources]

* [RFC 6797, HTTP Strict Transport Security](https://datatracker.ietf.org/doc/html/rfc6797)
* [W3C, Upgrade Insecure Requests](https://www.w3.org/TR/upgrade-insecure-requests/)
* [MDN, Strict-Transport-Security](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security)
* [MDN, upgrade-insecure-requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/upgrade-insecure-requests)
* [MDN, Mixed content](https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Mixed_content)
* [hstspreload.org, exigences de soumission et retrait](https://hstspreload.org/)
* [OWASP, HTTP Strict Transport Security cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html)

## Articles liés [#articles-liés]

* [Améliorer votre note de headers de sécurité](/fr/blog/improve-security-headers-grade)
* [Headers de sécurité hérités à retirer](/fr/blog/legacy-security-headers-to-retire)
* [Référence Strict-Transport-Security](/fr/docs/web-security/security-headers/strict-transport-security)
* [Référence upgrade-insecure-requests](/fr/docs/web-security/policies/content-security-policy/directives/upgrade-insecure-requests)
