Paramètres SameSite des cookies : Secure, portée du domaine et sous-domaines
Explication des paramètres SameSite des cookies : corrigez Secure, HttpOnly, la portée du domaine et le comportement sur les sous-domaines pour que les connexions fonctionnent en production.

Pourquoi l'authentification casse en production quand les cookies sont mal configurés
Quand la connexion fonctionne sur localhost mais échoue sur votre vrai domaine, le problème n'est souvent pas le mot de passe ni la base de données. C'est le cookie.
Les tests locaux sont indulgents : tout se passe sur un seul hôte, vous pouvez être en HTTP, et les redirections sont plus simples. En production, les navigateurs appliquent des règles plus strictes. De petits détails des cookies déterminent si le navigateur renverra votre session au serveur.
Les symptômes peuvent sembler sans rapport :
- le formulaire de connexion est soumis, mais vous retombez sur la page de connexion
- une boucle de redirection entre
/loginet/app - des sessions qui disparaissent après un refresh
- un navigateur fonctionne tandis qu'un autre échoue
Les environnements de production ajoutent des éléments variables : HTTPS, CDN ou proxies, sous-domaines séparés (comme app.example.com et api.example.com), et des redirections via des tiers (OAuth, pages de paiement, liens magiques par e-mail). C'est là que SameSite, Secure et la portée du cookie deviennent importants.
Un cookie n'est pas juste « un token de session ». C'est aussi un ensemble de règles qui dit au navigateur quand renvoyer ce token.
Une mauvaise configuration casse généralement l'auth en quelques façons prévisibles :
- Le navigateur n'envoie jamais le cookie sur la requête qui crée ou vérifie la session (souvent
SameSiteou la portée du domaine). - Le navigateur rejette le cookie parce que
Securen'est pas défini alors qu'il devrait l'être. - Le cookie est limité au mauvais Domain ou Path, donc il n'atteint pas les routes qui en ont besoin.
- Deux cookies du même nom existent (ancien et nouveau, staging et prod), et le navigateur envoie un autre que celui attendu.
Exemple : tout fonctionne sur http://localhost:3000, mais après déploiement sur app.yourdomain.com, votre API définit un cookie de session host-only pour api.yourdomain.com. Votre frontend ne l'envoie jamais, donc chaque chargement de page semble déconnecté.
Notions de base sur les cookies qui comptent pour l'auth
La plupart des expériences « connecté » reposent toujours sur un cookie qui représente la session. Après avoir saisi votre mot de passe, le serveur envoie un cookie dans la réponse et le navigateur le stocke. Lors des requêtes suivantes, le navigateur attache automatiquement ce cookie pour que le serveur vous reconnaisse.
Beaucoup de confusion vient du mélange de trois idées liées :
- Cookie de session : un identifiant que le serveur consulte (souvent stocké en base ou cache).
- Access token : un credential de courte durée (souvent utilisé avec des APIs).
- Refresh token : un credential plus long pour obtenir un nouvel access token.
Les bugs d'auth en production apparaissent souvent quand une appli attend une approche mais en implémente une autre. Par exemple, le frontend attend une session par cookie, mais le backend émet des tokens destinés à l'en-tête Authorization.
Pour l'auth basée sur cookie, les attributs comptent autant que la valeur :
SameSite: contrôle quand les cookies sont envoyés en contextes cross-site.Secure: le cookie n'est envoyé qu'en HTTPS.HttpOnly: empêche JavaScript de lire le cookie.Domain: quels hostnames peuvent le recevoir.Path: quelles URL paths peuvent le recevoir.
Les navigateurs n'envoient pas toujours les cookies. Ils ne les envoient que quand le domain et le path correspondent, que la connexion respecte Secure, et que le contexte de la requête passe les règles SameSite.
Un échec classique en production : en local tout est localhost, mais en production le frontend est sur https://app.example.com et l'API sur https://api.example.com. Si Domain ou SameSite est même légèrement incorrect, vous obtenez une boucle de connexion.
SameSite : ce que ça fait et quand ça bloque la connexion
SameSite est un flag de cookie qui dit au navigateur quand il est autorisé à envoyer votre cookie de session. Beaucoup de problèmes « marche en local, casse en prod » viennent d'un SameSite qui ne correspond pas au vrai flow de connexion.
Lax vs Strict vs None (en clair)
SameSite=Strict est le plus verrouillé. Le cookie n'est envoyé que dans un contexte first-party. Si un utilisateur arrive depuis un autre site, le navigateur peut retenir le cookie.
SameSite=Lax est le réglage par défaut qui convient à beaucoup d'apps. Le cookie est généralement envoyé lors de navigations top-level normales (comme cliquer sur un lien), mais pas lors de la plupart des requêtes background cross-site.
SameSite=None signifie « autoriser ce cookie en contexte cross-site ». Les navigateurs modernes exigent Secure quand on utilise None, sinon ils retirent le cookie.
Ce qui déclenche souvent des pannes
Un SameSite trop strict casse souvent :
- les connexions OAuth (Google, GitHub) où l'utilisateur est redirigé vers le callback
- les apps embarquées (votre app dans un iframe sur un autre domaine)
- des flows où le navigateur considère votre frontend et votre API comme cross-site
Les comportements par défaut des navigateurs ont aussi changé dans le temps. Les cookies sans SameSite explicite peuvent être traités comme Lax, et les cookies à None sans Secure sont souvent rejetés.
Quand avez-vous réellement besoin de SameSite=None ? Utilisez-le seulement si le cookie de session doit être envoyé dans un contexte cross-site (iframes, setups truly cross-site, certains flux basés sur des redirections). Si tout est same-site, Lax suffit souvent et réduit les risques.
Cookies Secure et HTTPS : l'incompatibilité la plus fréquente en production
Un cookie marqué Secure n'est envoyé qu'en HTTPS. Ça paraît simple, mais c'est une des raisons principales pour lesquelles une connexion fonctionne en local puis échoue après déploiement.
Une règle piège : si vous utilisez SameSite=None, les navigateurs modernes exigent aussi que le cookie soit Secure. Sans cela, le navigateur peut silencieusement supprimer le cookie.
Le développement local induit souvent en erreur. Beaucoup d'apps tournent sur http://localhost, donc Secure est omis pour faciliter les tests. Puis en production vous activez SameSite=None pour une redirection ou un embed, et le cookie ne tient pas.
Les proxies et CDN compliquent le tableau. Votre navigateur est en HTTPS, mais votre serveur peut voir la requête entrante en HTTP parce que le TLS est terminé au proxy. Si l'app pense « je suis en HTTP », elle peut refuser de définir Secure, ou générer des redirections qui alternent entre HTTP et HTTPS.
Si les cookies disparaissent seulement sur le site live, vérifiez :
- Dans DevTools, confirmez que le cookie d'auth a
Secureet que l'URL de la requête esthttps://... - Recherchez des avertissements comme « cookie was blocked because it had SameSite=None without Secure »
- Vérifiez les en-têtes proxy (souvent
X-Forwarded-Proto: https) et que votre framework fait confiance au proxy - Assurez-vous que les utilisateurs ne peuvent pas atteindre une version HTTP du site
- Confirmez que le cookie est défini sur le domaine final après redirections, pas sur un hostname intermédiaire
HttpOnly et CSRF : rendre le cookie plus sûr sans casser l'UX
Si votre cookie de session est lisible en JavaScript, toute faille XSS peut se transformer en prise de contrôle de compte. C'est pourquoi HttpOnly est important. Avec HttpOnly, le navigateur continue d'envoyer le cookie sur les requêtes, mais document.cookie ne peut pas le lire.
Stocker l'auth en cookie lisible en JS peut sembler pratique pour une SPA, mais ça ajoute généralement du risque sans véritable bénéfice. Si le frontend doit savoir si l'utilisateur est connecté, appelez un endpoint léger comme /me et laissez le serveur décider à partir du cookie.
Le CSRF est l'autre moitié de l'histoire. Les cookies sont envoyés automatiquement, donc un site malveillant peut tenter de déclencher des requêtes depuis un navigateur connecté. SameSite aide, mais les requêtes qui modifient l'état doivent toujours avoir une protection CSRF.
Règles pratiques pour une UX simple tout en augmentant la sécurité :
- Conservez le token de session uniquement dans un cookie
HttpOnly(pas dans localStorage). - Utilisez une protection CSRF pour les requêtes qui modifient l'état.
- Limitez la portée des cookies (hôte et path) pour que moins de requêtes portent la session.
Un compromis : si vous utilisez des cookies pour l'auth d'une API, votre SPA ne peut pas les attacher manuellement. Elle dépend du navigateur, donc CORS et les réglages credentials doivent être corrects.
Portée Domain et Path : empêcher les cookies d'aller au mauvais endroit
Les bugs de cookie ressemblent souvent à des « déconnexions aléatoires » ou « ça marche en local mais pas après déploiement ». Domain et Path décident où le navigateur enverra votre cookie d'auth. S'ils ne correspondent pas à vos URL réelles, le serveur ne verra jamais le cookie et considérera l'utilisateur comme déconnecté.
Même un SameSite parfait ne servira à rien si le cookie n'est pas envoyé du tout.
Cookies host-only vs Domain
Si vous ne définissez pas l'attribut Domain, vous obtenez un cookie host-only. Il est verrouillé à l'hôte exact qui l'a créé, comme app.example.com.
Si vous définissez Domain=.example.com, le cookie devient disponible pour des sous-domaines comme app.example.com et api.example.com. Utile parfois, mais cela élargit aussi où le cookie peut être envoyé et augmente le risque qu'un sous-domaine écrase un autre cookie du même nom.
Scope Path dans des setups multi-app
Path est le « dossier » URL où le cookie est valide. Path=/ envoie le cookie à tous les chemins de cet hôte. Path=/app l'envoie seulement à /app et en dessous.
Une erreur courante est de définir le cookie sur Path=/api parce qu'il a été créé sur une route API, puis de se demander pourquoi les pages sous /app ne restent pas connectées.
Vérifications rapides qui évitent beaucoup de problèmes en production :
- Choisissez un hôte canonique (
wwwou non-www) et tenez-vous-y. - Si vous avez besoin d'auth partagée entre sous-domaines, utilisez un cookie de domaine parent. Sinon, laissez
Domainnon défini. - Utilisez
Path=/sauf raison claire de l'isoler. - Évitez de réutiliser le même nom de cookie entre différentes apps avec des scopes différents.
- Vérifiez quel hôte définit réellement le cookie après les redirections.
Sous-domaines : comportement prévisible entre app et API
Une configuration commune est app.example.com pour le frontend et api.example.com pour le backend. Ça a l'air simple, mais les règles de cookie peuvent changer entre le dev local et la production une fois HTTPS, proxies et vrais domaines impliqués.
Si un cookie est host-only, un cookie défini par api.example.com ne sera pas envoyé à app.example.com, et inversement. Si vous avez vraiment besoin d'un cookie partagé, il faut généralement le scoper au domaine parent.
Avant de partager des cookies entre sous-domaines, confirmez :
- Avez-vous réellement besoin du même cookie sur les deux sous-domaines, ou l'API peut-elle être seule à le définir et le lire ?
- Utilisez-vous HTTPS de bout en bout, y compris derrière un load balancer ou CDN ?
- Vos réglages
SameSitecorrespondent-ils à la façon dont le navigateur classe vos requêtes (same-site vs cross-site) ?
Rappelez-vous aussi : CORS et cookies doivent être cohérents. Même si le cookie est correctement scoped, le navigateur ne l'attachera pas aux requêtes API à moins que votre fetch/XHR n'utilise credentials et que l'API autorise les credentials avec une origine explicite.
Partager des cookies entre sous-domaines n'en vaut pas toujours la peine. Un cookie de domaine large augmente les endroits où il peut être envoyé. Gardez les cookies d'auth aussi restreints que possible et élargissez la portée seulement quand c'est nécessaire.
Étape par étape : une configuration sûre pour les navigateurs modernes
Une configuration sûre commence par votre parcours utilisateur réel, pas par un réglage framework par défaut. Ce qui marche sur localhost peut échouer après déploiement parce que la production ajoute HTTPS, redirections et hôtes séparés.
Checklist pratique
Cartographiez votre flow de connexion de bout en bout, puis faites en sorte que le cookie corresponde :
- Cartographiez le flow réel (y compris les redirections). Notez chaque étape : app, fournisseur d'auth, URL de callback, dashboard.
- Décidez où la session doit vivre. Choisissez un endroit pour définir et lire le cookie de session de façon cohérente (souvent l'API).
- Choisissez
SameSiteen fonction de ce flow. UtilisezNoneseulement si vous avez vraiment besoin d'envoyer des cookies cross-site. Sinon,Laxsuffit la plupart du temps. - Forcez HTTPS et définissez
SecureetHttpOnly. Si vous utilisezSameSite=None, vous devez utiliserSecure. - Définissez
DomainetPathintentionnellement. Élargissez la portée seulement si nécessaire. GardezPath=/sauf raison claire de le limiter.
Validez sur le vrai domaine de production en fenêtre incognito : connectez-vous, faites un hard refresh, ouvrez un nouvel onglet, et confirmez que le cookie est présent et envoyé sur les requêtes importantes.
Erreurs courantes qui causent des bugs d'auth difficiles à déboguer
Les bugs de cookie semblent aléatoires parce qu'ils apparaissent souvent seulement après le déploiement. De petites différences (HTTPS, un vrai domaine, un proxy, un hôte API séparé) font que les règles du navigateur comptent.
Les erreurs derrière la plupart des boucles de connexion et des « déconnecté après refresh » :
- Définir
SameSite=Nonemais oublierSecure(le navigateur peut jeter le cookie). - Définir
Domaintrop large et partager involontairement des cookies entre sous-domaines. - Mélanger HTTP et HTTPS derrière un proxy, de sorte que l'app définit des cookies ou redirections basées sur le mauvais schéma.
- Se fier au comportement de localhost, qui ne correspond pas aux vrais domaines.
- Traiter le CORS comme le seul problème. CORS peut permettre une requête, mais les cookies peuvent toujours être bloqués par la portée ou
SameSite.
Exemple : vous déployez app.example.com (frontend) et api.example.com (backend). Vous mettez à jour SameSite pour autoriser le comportement cross-site mais oubliez Secure. En production, le navigateur jette le cookie de session et chaque refresh ressemble à un nouvel utilisateur.
Vérifications rapides avant de livrer (et quand vous recevez un bug report)
La plupart des bugs de cookie deviennent évidents une fois que vous vérifiez trois endroits : le stockage des cookies du navigateur, la requête réseau, et la raison côté serveur pour laquelle la session est rejetée.
Dans DevTools (Application/Storage), cliquez le cookie et vérifiez :
- Le cookie est-il présent après la connexion et persiste-t-il après un refresh ?
SameSite,Secure,HttpOnly,DomainetPathsont-ils ceux attendus ?ExpiresouMax-Ageont-ils une valeur raisonnable ?- Définissez-vous accidentellement deux cookies du même nom sur des domaines différents ?
- Le cookie est-il défini par l'hôte que vous pensez (app vs api) ?
Ensuite, vérifiez l'onglet Network. Inspectez une requête authentifiée (comme GET /me). Si le cookie existe en stockage mais n'est pas sur la requête, soit les attributs le bloquent, soit la requête va vers un hôte différent de celui attendu.
Enfin, vérifiez HTTPS de bout en bout. Si le TLS se termine sur un load balancer mais que les en-têtes proxy manquent, l'app peut définir des cookies non-Secure ou générer des redirections qui cassent la session.
Quand c'est possible, journalisez une raison serveur claire pour le rejet de session : cookie manquant, session expirée, signature invalide, échec CSRF, etc.
Exemple : boucle de connexion après déploiement due à SameSite et Domain
Une histoire courante : OAuth fonctionne en local, puis casse après le déploiement sur https://app.example.com. Les utilisateurs cliquent sur « Continue with Google », sont redirigés en retour, semblent brièvement connectés, puis retombent sur la page de login. En boucle.
Ce qui se passe est généralement un décalage de cookie lors du redirect OAuth et entre vos sous-domaines.
En local, votre app et votre API peuvent être sur localhost, donc le navigateur envoie le cookie partout. Après déploiement, le flow ressemble souvent à ceci :
-
Le fournisseur OAuth redirige l'utilisateur vers
https://app.example.com/auth/callback. -
Le callback définit un cookie de session, mais il est host-only pour
app.example.com(sans attributDomain). -
Le frontend appelle
https://api.example.com/mepour récupérer l'utilisateur connecté. -
Le navigateur n'envoie pas le cookie à
api.example.com, donc l'API renvoie 401. L'app interprète ça comme « pas connecté » et relance le flow de connexion.
SameSite peut aggraver le problème. Si le cookie utilisé pendant le callback OAuth est SameSite=Strict, le navigateur peut ne pas l'envoyer lors du redirect cross-site depuis le fournisseur, si bien que votre serveur ne peut pas associer l'état et le flow échoue.
Plan de correction pratique :
- Définir
Secure=trueen production (surtout si vous utilisezSameSite=None). - Utiliser
SameSite=Laxpour les sessions de connexion typiques et les cookies d'état OAuth (il survit généralement aux redirections top-level). - Si l'API a vraiment besoin du même cookie, scopez-le au domaine parent et gardez le
Pathcohérent.
Pour confirmer la correction sans deviner :
- Vérifiez l'entrée du cookie pour
Domain,Path,SameSiteetSecure. - Sur la requête API qui échoue, vérifiez si l'en-tête
Cookieest présent. - Recherchez des notes « blocked » dans le panneau cookie (les navigateurs expliquent souvent pourquoi un cookie a été rejeté).
Étapes suivantes : consolider et demander de l'aide si c'est toujours instable
Une fois que vous avez trouvé une configuration de cookie qui fonctionne, consignez-la comme une règle, pas comme une supposition : les valeurs exactes pour SameSite, Secure, HttpOnly, Domain et Path, plus ce qui change entre local, staging et production.
Gardez un petit plan de test à répéter avant chaque release : connexion, refresh, nouvel onglet, incognito et un second navigateur (Chrome et Safari montrent souvent les différences rapidement).
Si vous travaillez sur un prototype généré par IA qui casse une fois sur de vrais domaines, c'est souvent l'une de quelques causes répétées : Secure manquant, Domain/Path mal alignés, cookies créés sur le mauvais hôte après des redirections, ou comportement cross-site bloqué par le navigateur.
Si vous voulez un second regard, FixMyMess (fixmymess.ai) se concentre sur la réparation d'apps générées par IA et leur mise en production, y compris le diagnostic des flows d'auth et de cookie de bout en bout. Un audit rapide suffit souvent à repérer l'attribut exact ou l'étape de redirection qui casse la session.
Questions Fréquentes
Why does login work on localhost but fail after I deploy?
Généralement, le navigateur ne renvoie pas le cookie de session sur votre domaine de production. En local tout partage le même hôte, mais en production HTTPS, les sous-domaines et les redirections activent des règles plus strictes : un SameSite, Secure, Domain ou Path légèrement incorrect peut faire croire au serveur que l'utilisateur n'est pas connecté.
What causes the “/login” ↔ “/app” redirect loop?
Une boucle de redirection signifie souvent que l'application crée un cookie pendant la connexion, mais que la requête suivante qui vérifie la session n'inclut pas ce cookie. Causes les plus courantes : cookie limité au mauvais hôte (app vs API), SameSite qui le bloque pendant une redirection, ou cookie rejeté parce que SameSite=None sans Secure.
What SameSite setting should I use for most auth cookies?
Commencez par SameSite=Lax pour une session web classique : il survit généralement aux navigations top-level et à beaucoup de redirections OAuth. N'utilisez SameSite=None que si vous avez vraiment besoin d'envoyer le cookie dans un contexte cross-site (iframes, setups cross-site), et toujours avec Secure activé.
Why does SameSite=None require Secure?
Les navigateurs modernes rejettent les cookies SameSite=None s'ils ne sont pas marqués Secure, c'est-à-dire envoyés uniquement sur HTTPS. Si vous oubliez Secure, le navigateur peut jeter le cookie silencieusement : il semble être défini par le serveur, mais n'est pas conservé.
Why do Secure cookies break behind a CDN or reverse proxy?
Secure signifie que le cookie n'est envoyé qu'en HTTPS. En production c'est souhaitable, mais un proxy peut terminer le TLS et transmettre une requête en HTTP à votre serveur, qui peut alors générer des cookies ou des redirections basées sur le mauvais schéma. La solution consiste à forcer HTTPS et à configurer la confiance des en-têtes proxy (par ex. X-Forwarded-Proto).
What’s the difference between a host-only cookie and a Domain cookie?
Un cookie host-only (sans attribut Domain) n'est envoyé qu'à l'hôte exact qui l'a créé, comme api.example.com. Un cookie de domaine (ex. .example.com) peut être envoyé à plusieurs sous-domaines. Si votre frontend est sur app.example.com et que le cookie est host-only pour api.example.com, le frontend ne l'enverra pas et les utilisateurs sembleront déconnectés.
How can the cookie Path make me randomly “lose” my session?
L'attribut Path restreint l'endroit où le cookie est envoyé sur le même hôte. Si vous définissez par erreur Path=/api, les pages sous /app ne recevront pas le cookie même si elles sont sur le même domaine, et les vérifications de session échoueront. Pour la plupart des cookies d'auth, Path=/ est la valeur par défaut la plus simple.
Should my session cookie be HttpOnly, and will that break my SPA?
Oui. Utilisez HttpOnly pour que JavaScript ne puisse pas lire le cookie de session, ce qui limite les dégâts en cas de XSS. Si le frontend a besoin de savoir si l'utilisateur est connecté, interrogez un endpoint serveur (par ex. /me) plutôt que de lire un token côté client.
How do I quickly debug whether the cookie is being set and sent?
Vérifiez d'abord si le cookie existe après la connexion et s'il persiste après un hard refresh. Ensuite, dans l'onglet Network, inspectez une requête authentifiée et confirmez que l'en-tête Cookie est présent. Si le cookie est stocké mais pas envoyé, c'est presque toujours dû à SameSite, Secure, Domain, Path ou à une requête vers un hôte différent de celui attendu.
My AI-generated prototype’s auth is flaky in production—what should I do next?
Les paramètres de cookie et d'auth dans les prototypes générés par IA sont souvent incohérents entre local et production, notamment avec des sous-domaines, callbacks OAuth et proxies. L'approche la plus rapide est un audit ciblé du flow de connexion sur votre domaine live pour repérer l'attribut ou l'étape de redirection qui fait disparaître la session. FixMyMess peut aussi examiner le code et réparer la configuration d'auth en 48–72 heures.