05 nov 2025·7 min de lectura

Configuración SameSite de cookies: Secure, alcance de dominio y subdominios

Configuración SameSite de cookies explicada: corrige Secure, HttpOnly, scope de dominio y comportamiento en subdominios para que los inicios de sesión funcionen en producción.

Configuración SameSite de cookies: Secure, alcance de dominio y subdominios

Por qué falla la autenticación en producción cuando las cookies están mal configuradas

Cuando el inicio de sesión funciona en localhost pero falla en tu dominio real, el problema suele no ser la comprobación de la contraseña ni la base de datos. Es la cookie.

Las pruebas locales son indulgentes: todo está en un mismo host, puede que uses HTTP y los redireccionamientos son más sencillos. En producción, los navegadores aplican reglas más estrictas. Detalles pequeños de la cookie deciden si el navegador enviará tu sesión de vuelta al servidor.

Los síntomas pueden parecer no relacionados:

  • el formulario de login se envía, pero vuelves a la página de inicio de sesión
  • un bucle de redirección entre /login y /app
  • sesiones que desaparecen tras un refresco
  • un navegador funciona mientras otro falla

Los entornos de producción añaden partes móviles: HTTPS, CDNs o proxies, subdominios separados (como app.example.com y api.example.com) y redirecciones a través de terceros (OAuth, páginas de pago, enlaces mágicos por email). Ahí es donde SameSite, Secure y el scope de la cookie empiezan a importar.

Una cookie no es solo "un token de sesión". También es un conjunto de reglas que le dice al navegador cuándo enviar ese token.

La mala configuración normalmente rompe la autenticación de formas previsibles:

  • El navegador nunca envía la cookie en la petición que crea o verifica la sesión (a menudo por SameSite o el scope de dominio).
  • El navegador rechaza la cookie porque no se marcó Secure cuando debería haberse marcado.
  • La cookie está limitada al Domain o Path equivocado, así que no llega a las rutas que la necesitan.
  • Existen dos cookies con el mismo nombre (antigua y nueva, staging y prod), y el navegador envía una distinta a la que esperas.

Ejemplo: todo funciona en http://localhost:3000, pero tras desplegar en app.yourdomain.com, tu API establece una cookie de sesión host-only para api.yourdomain.com. Tu frontend nunca la envía, así que cada carga de página aparece como sin sesión.

Conceptos básicos de cookies que importan para la autenticación

La mayoría de experiencias “logueado” aún dependen de una cookie que representa tu sesión. Después de introducir la contraseña, el servidor envía una cookie en la respuesta y el navegador la guarda. En peticiones posteriores, el navegador adjunta esa cookie automáticamente para que el servidor pueda reconocerte.

Mucha confusión viene de mezclar tres ideas relacionadas:

  • Cookie de sesión: un identificador que el servidor consulta (a menudo almacenado en BD o caché).
  • Access token: una credencial de corta vida (a menudo usada con APIs).
  • Refresh token: una credencial de más larga duración usada para obtener un nuevo access token.

Los bugs de auth en producción suelen aparecer cuando una app espera un enfoque pero implementa otro. Por ejemplo, el frontend espera sesiones basadas en cookies, pero el backend emite tokens pensados para el header Authorization.

Para la autenticación basada en cookies, los atributos importan tanto como el valor:

  • SameSite: controla cuándo se envían las cookies en contextos cross-site.
  • Secure: la cookie solo se envía por HTTPS.
  • HttpOnly: evita que JavaScript lea la cookie.
  • Domain: qué hostnames pueden recibirla.
  • Path: qué rutas URL pueden recibirla.

Los navegadores no siempre envían cookies. Solo las envían cuando dominio y path coinciden, la conexión cumple Secure y el contexto de la petición pasa las reglas de SameSite.

Un fallo clásico solo en producción: localmente todo es localhost, pero en producción el frontend está en https://app.example.com y la API en https://api.example.com. Si Domain o SameSite están aunque sea ligeramente mal, obtendrás un bucle de login.

SameSite: qué hace y cuándo bloquea tu login

SameSite es un flag de cookie que le indica al navegador cuándo está permitido enviar tu cookie de sesión. Muchos problemas de “funciona localmente, falla en producción” vienen de configuraciones SameSite que no coinciden con el flujo real de inicio de sesión.

Lax vs Strict vs None (en términos sencillos)

SameSite=Strict es el más restringido. La cookie solo se envía en un contexto de primer partido. Si un usuario llega desde otro sitio, el navegador puede retener la cookie.

SameSite=Lax es la opción por defecto que funciona para muchas apps. La cookie normalmente se envía en navegaciones de primer nivel (como al hacer clic en un enlace), pero no en la mayoría de peticiones en segundo plano cross-site.

SameSite=None significa “permitir esta cookie en contextos cross-site”. Los navegadores modernos requieren Secure cuando usas None, o descartarán la cookie.

Qué suele desencadenar fallos

SameSite demasiado estricto suele romper:

  • Logins por OAuth (Google, GitHub) donde el usuario es redirigido de vuelta
  • Apps embebidas (tu app dentro de un iframe en otro dominio)
  • Flujos donde el navegador clasifica tu frontend y tu API como cross-site

Los valores por defecto de los navegadores también han cambiado con el tiempo. Las cookies sin SameSite explícito pueden tratarse como Lax, y las cookies con None sin Secure suelen ser rechazadas.

¿Cuándo necesitas realmente SameSite=None? Úsalo solo cuando la cookie de sesión deba enviarse en un contexto cross-site (iframes, setups cross-site genuinos, algunos flujos con redirecciones). Si todo es same-site, Lax suele ser suficiente y reduce el riesgo.

Cookies Secure y HTTPS: el desajuste más común en producción

Una cookie marcada Secure solo se envía por HTTPS. Parece simple, pero es una de las principales razones por las que los inicios de sesión funcionan localmente y fallan tras el despliegue.

Una regla suele pillar a la gente por sorpresa: si usas SameSite=None, los navegadores modernos requieren que la cookie también sea Secure. Sin ello, el navegador puede descartar la cookie silenciosamente.

El desarrollo local confunde a muchos equipos. Muchas apps corren en http://localhost, así que se omite Secure para facilitar las pruebas. Luego en producción se usa HTTPS, alguien activa SameSite=None para un redirect o flujo embebido, y la cookie nunca se mantiene.

Los proxies y CDNs pueden empeorar esto. Tu navegador está en HTTPS, pero tu servidor de aplicaciones puede ver la petición entrante como HTTP porque el TLS termina en el proxy. Si la app piensa “estoy en HTTP”, puede negarse a marcar Secure, o generar redirecciones que reboten entre HTTP y HTTPS.

Si las cookies desaparecen solo en el sitio en vivo, revisa:

  • En DevTools, confirma que la cookie de auth muestra Secure y que la URL de la petición es https://...
  • Busca advertencias como “cookie was blocked because it had SameSite=None without Secure”
  • Verifica los headers del proxy (a menudo X-Forwarded-Proto: https) y que tu framework confíe en el proxy
  • Asegúrate de que los usuarios no lleguen a una versión HTTP del sitio
  • Confirma que la cookie se establece en el dominio final después de redirecciones, no en un hostname intermedio

Si tu cookie de sesión es legible por JavaScript, cualquier bug XSS puede convertirse en una toma completa de cuenta. Por eso HttpOnly importa. Con HttpOnly, el navegador sigue enviando la cookie en las peticiones, pero document.cookie no puede leerla.

Almacenar autenticación en una cookie accesible por JS suele parecer cómodo para una SPA, pero normalmente añade riesgo sin beneficio real. Si el frontend necesita saber si el usuario está autenticado, llama a un endpoint ligero como /me y deja que el servidor decida en base a la cookie.

CSRF es la otra parte de esta historia. Las cookies se envían automáticamente, así que un sitio malicioso puede intentar provocar peticiones desde un navegador autenticado. SameSite ayuda, pero las peticiones que cambian estado aún necesitan protección CSRF.

Reglas prácticas que mantienen la UX simple y elevan la seguridad:

  • Mantén el token de sesión solo en una cookie HttpOnly (no en localStorage).
  • Usa protección CSRF para peticiones que cambian estado.
  • Limita el scope de las cookies (host y path) para que menos peticiones lleven tu sesión.

Un compromiso: si usas cookies para auth de API, tu SPA no puede adjuntarlas manualmente. Depende del navegador, por lo que CORS y la configuración de credentials deben ser correctas.

Alcance de Domain y Path: evita que las cookies vayan al lugar equivocado

Haz que la autenticación funcione en subdominios
Diagnosticamos el alcance de las cookies entre app y API y lo corregimos para que las sesiones persistan.

Los bugs de cookies a menudo parecen “desconexiones aleatorias” o “funciona en local pero no después del despliegue”. Domain y Path deciden dónde el navegador enviará tu cookie de auth. Si no coinciden con tus URLs reales, tu servidor nunca verá la cookie y tratará al usuario como desconectado.

Incluso un SameSite perfecto no ayuda si la cookie no se envía en absoluto.

Cookies host-only vs de dominio

Si no estableces el atributo Domain, obtienes una cookie host-only. Está bloqueada al host exacto que la estableció, como app.example.com.

Si pones Domain=.example.com, la cookie estará disponible para subdominios como app.example.com y api.example.com. Eso puede ser útil, pero también amplía dónde la cookie puede enviarse y aumenta la posibilidad de que un subdominio sobrescriba otra cookie con el mismo nombre.

Path en setups con múltiples apps

Path es la "carpeta" URL donde la cookie es válida. Path=/ envía la cookie a cada ruta en ese host. Path=/app la envía solo a /app y subrutas.

Un error común es establecer la cookie en Path=/api porque se creó en una ruta de la API, y luego preguntarse por qué las páginas bajo /app no se mantienen logueadas.

Comprobaciones rápidas que previenen muchos problemas en producción:

  • Elige un host canónico (www o sin www) y mantenlo.
  • Si necesitas auth compartida entre subdominios, usa una cookie en el dominio padre. Si no, deja Domain sin establecer.
  • Usa Path=/ salvo que tengas una razón clara para aislar cookies.
  • Evita reutilizar el mismo nombre de cookie entre apps diferentes con scopes distintos.
  • Verifica qué host realmente establece la cookie después de redirecciones.

Subdominios: comportamiento predecible entre app y API

Un setup común es app.example.com para el frontend y api.example.com para el backend. Parece simple, pero las reglas de cookies pueden cambiar entre el desarrollo local y producción una vez que entran HTTPS, proxies y dominios reales.

Si una cookie es host-only, una cookie establecida por api.example.com no se enviará a app.example.com, y viceversa. Si necesitas que una cookie funcione en ambos, normalmente la limitas al dominio padre.

Antes de compartir cookies entre subdominios, confirma:

  • ¿Realmente necesitas la misma cookie en ambos subdominios, o puede la API ser el único lugar que la lee y la escribe?
  • ¿Usas HTTPS de extremo a extremo, incluido detrás de un load balancer o CDN?
  • ¿Tus settings de SameSite coinciden con cómo el navegador clasifica tus peticiones (same-site vs cross-site)?

Recuerda también: CORS y cookies deben coincidir. Aunque la cookie esté correctamente scopeada, el navegador no la adjuntará a peticiones de API a menos que tu fetch/XHR use credentials y la API permita credentials con un origin explícito.

Compartir cookies entre subdominios no siempre merece la pena. Una cookie de dominio amplia aumenta dónde puede enviarse. Mantén las cookies de auth lo más ajustadas posible y amplía el scope solo cuando haya una necesidad clara.

Paso a paso: una configuración segura de cookies para navegadores modernos

Detecta riesgos de autenticación temprano
Escaneamos tu app creada por IA en busca de claves filtradas y patrones de autenticación riesgosos.

Una configuración segura de cookies comienza por tu flujo real de usuario, no por un ajuste por defecto del framework. Lo que funciona en localhost puede fallar tras el despliegue porque producción añade HTTPS, redirecciones y hosts separados.

Lista práctica de comprobación

Mapea tu flujo de login de principio a fin y ajusta la cookie a ese flujo:

  • Mapea el flujo real de login (incluyendo redirecciones). Anota cada salto: app, proveedor de auth, URL de callback, dashboard.
  • Decide dónde debe vivir la sesión. Elige un único lugar para establecer y leer la cookie de sesión de forma consistente (a menudo la API).
  • Elige SameSite según ese flujo. Usa None solo si realmente necesitas envío cross-site. De lo contrario, Lax suele ser suficiente.
  • Forza HTTPS y marca Secure y HttpOnly. Si usas SameSite=None, debes usar Secure.
  • Establece Domain y Path intencionalmente. Amplía el scope solo cuando haga falta. Mantén Path=/ salvo que haya una razón clara para limitarlo.

Valida en el dominio real de producción en una ventana de incógnito: inicia sesión, refresca a fondo, abre una nueva pestaña y confirma que la cookie está presente y se envía en las peticiones que importan.

Errores comunes que causan bugs difíciles de depurar

Los bugs de cookies se sienten aleatorios porque a menudo aparecen solo después del despliegue. Pequeñas diferencias (HTTPS, un dominio real, un proxy, un host de API separado) hacen que las reglas del navegador sean relevantes.

Los errores detrás de la mayoría de bucles de login y reportes de “desconectado después de refrescar”:

  • Poner SameSite=None y olvidar Secure (el navegador puede descartar la cookie).
  • Poner Domain demasiado amplio y compartir cookies sin querer con otros subdominios.
  • Mezclar HTTP y HTTPS detrás de un proxy, de modo que la app establece cookies o redirecciones basadas en el esquema equivocado.
  • Confiar en el comportamiento de localhost, que no coincide con dominios reales.
  • Tratar CORS como el único problema. CORS puede permitir una petición, pero las cookies aún pueden bloquearse por scope o SameSite.

Ejemplo: despliegas app.example.com (frontend) y api.example.com (backend). Actualizas SameSite para permitir comportamiento cross-site pero olvidas Secure. En producción, el navegador descarta la cookie de sesión y cada refresco parece un usuario nuevo.

Comprobaciones rápidas antes de publicar (y cuando te llega un bug)

La mayoría de bugs de cookies se hacen evidentes cuando inspeccionas tres lugares: el almacenamiento de cookies del navegador, la petición de red y la razón del servidor para rechazar la sesión.

En DevTools (Application/Storage), haz clic en la cookie y verifica:

  • ¿La cookie está presente después del login y persiste tras un refresh?
  • ¿SameSite, Secure, HttpOnly, Domain y Path son los que esperas?
  • ¿Expires o Max-Age parecen razonables?
  • ¿Estás accidentalmente seteando dos cookies con el mismo nombre en distintos dominios?
  • ¿La cookie la está estableciendo el host que crees (app vs api)?

Luego revisa la pestaña Network. Inspecciona una petición autenticada (como GET /me). Si la cookie existe en el almacenamiento pero no aparece en la petición, o bien los atributos la están bloqueando o la petición va a un host distinto del que crees.

Finalmente, verifica HTTPS de extremo a extremo. Si TLS termina en un load balancer pero faltan headers de proxy, la app puede establecer cookies no-Secure o generar redirecciones que rompan la sesión.

Cuando puedas, registra en el servidor una razón clara para el rechazo de la sesión: cookie faltante, sesión expirada, firma inválida, fallo CSRF, etc.

Ejemplo: bucle de login tras el despliegue por SameSite y dominio

Limpia la autenticación inestable
Reemplaza código de sesión desordenado por flujos claros que no fallen al refrescar.

Una historia común: el login por OAuth funciona en localhost, luego falla tras desplegar en https://app.example.com. Los usuarios hacen clic en "Continue with Google", son redirigidos de vuelta, parecen loguearse brevemente y luego vuelven a la página de login. Una y otra vez.

Lo que suele ocurrir es un desajuste de cookies durante la redirección de OAuth y entre subdominios.

Localmente, tu app y API pueden estar en localhost, así que el navegador envía la cookie a todas partes. Tras desplegar, el flujo suele verse así:

  1. El proveedor OAuth redirige al usuario a https://app.example.com/auth/callback.

  2. El callback establece una cookie de sesión, pero es host-only para app.example.com (sin atributo Domain).

  3. El frontend llama a https://api.example.com/me para obtener el usuario logueado.

  4. El navegador no envía la cookie a api.example.com, así que la API devuelve 401. La app interpreta que no está logueado y reinicia el flujo de login.

SameSite puede empeorar esto. Si la cookie usada durante el callback de OAuth es SameSite=Strict, el navegador puede no enviarla en la redirección cross-site desde el proveedor, así que tu servidor no puede emparejar el state y el flujo falla.

Plan de arreglos práctico:

  • Marca Secure=true en producción (especialmente si usas SameSite=None).
  • Usa SameSite=Lax para sesiones típicas y cookies de estado de OAuth (suele sobrevivir a redirecciones de primer nivel).
  • Si la API realmente necesita la misma cookie, establécela en el dominio padre y mantén el Path consistente.

Para confirmar la solución sin adivinar:

  • Comprueba la entrada de cookie para ver Domain, Path, SameSite y Secure.
  • En la petición de API que falla, verifica si el header Cookie está presente.
  • Busca notas de “blocked” en el panel de cookies (los navegadores suelen explicar por qué se rechazó una cookie).

Próximos pasos: fijarlo y pedir ayuda si sigue siendo inestable

Una vez encuentres una configuración de cookies que funcione, escríbela como una regla, no como una suposición: los valores exactos para SameSite, Secure, HttpOnly, Domain y Path, y qué cambia entre local, staging y producción.

Mantén un pequeño plan de pruebas que repitas antes de releases: login, refresh, nueva pestaña, ventana de incógnito y un segundo navegador (Chrome y Safari suelen revelar diferencias rápidamente).

Si trabajas con un prototipo generado por IA que falla al estar en un dominio real, suele deberse a unos pocos culpables recurrentes: falta de Secure, Domain/Path desajustados, cookies establecidas en el host equivocado tras redirecciones o comportamiento cross-site que el navegador bloquea.

Si quieres una segunda opinión, FixMyMess (fixmymess.ai) se especializa en tomar apps generadas por IA y dejarlas listas para producción, incluyendo diagnosticar flujos de cookies y auth de punta a punta. Una auditoría rápida suele ser suficiente para señalar el atributo o el paso de redirección exacto que rompe la sesión.

Preguntas Frecuentes

¿Por qué funciona el inicio de sesión en localhost pero falla después de desplegar?

Normalmente el navegador no está devolviendo la cookie de sesión en tu dominio de producción. En localhost todo comparte el mismo host, pero en producción HTTPS, subdominios y redirecciones activan reglas más estrictas, de modo que un SameSite, Secure, Domain o Path ligeramente incorrecto puede hacer que el servidor piense que no estás autenticado.

¿Qué causa el bucle de redirección “/login” ↔ “/app”?

Un bucle de redirección suele significar que la app establece una cookie durante el login, pero la siguiente solicitud que verifica la sesión no incluye esa cookie. Las causas más comunes son: la cookie está limitada al host equivocado (app vs subdominio de la API), SameSite la bloquea durante un flujo basado en redirecciones, o la cookie se descarta porque usa SameSite=None sin Secure.

¿Qué configuración de SameSite debería usar para la mayoría de cookies de autenticación?

Empieza con SameSite=Lax para una sesión típica de una web, porque suele sobrevivir a navegaciones de primer nivel y a muchos redireccionamientos de OAuth. Usa SameSite=None solo si realmente necesitas que la cookie se envíe en contextos cross-site (iframes o setups realmente cross-site), y siempre con Secure activado.

¿Por qué SameSite=None requiere Secure?

Los navegadores modernos rechazan las cookies con SameSite=None a menos que también tengan Secure, es decir, que solo se envíen por HTTPS. Si olvidas Secure, la cookie puede parecer que la ha devuelto el servidor, pero el navegador la descarta silenciosamente y cada actualización parece una sesión cerrada.

¿Por qué las cookies Secure fallan detrás de un CDN o proxy inverso?

Secure significa que la cookie solo se envía por HTTPS. En producción eso es lo deseable, pero los proxies pueden confundir a tu aplicación haciéndola creer que la petición llegó por HTTP, lo que altera cómo se generan cookies y redirecciones. La solución suele ser forzar HTTPS y asegurarse de que el backend confíe en los headers del proxy.

¿Cuál es la diferencia entre una cookie host-only y una cookie de dominio?

Una cookie host-only (sin el atributo Domain) solo se envía al host exacto que la creó, por ejemplo api.example.com. Una cookie de dominio (p. ej. .example.com) puede enviarse a varios subdominios. Si tu frontend está en app.example.com y la cookie es host-only para api.example.com, el frontend no la enviará en peticiones a app.example.com, lo que suele generar el comportamiento de “desconectado después de refrescar”.

¿Cómo puede el Path hacer que “pierda” la sesión aleatoriamente?

El atributo Path restringe dónde se envía la cookie en el mismo host. Si por error pones Path=/api, las páginas bajo /app no recibirán la cookie aunque estén en el mismo dominio, y las comprobaciones de sesión fallarán. Para la mayoría de cookies de autenticación, Path=/ es la opción más simple salvo que haya una razón clara para aislarla.

¿Debería mi cookie de sesión ser HttpOnly y esto romperá mi SPA?

Sí: usa HttpOnly para que JavaScript no pueda leer la cookie de sesión, lo que reduce el daño ante un XSS. Si el frontend necesita saber si el usuario está autenticado, consulta un endpoint del servidor (por ejemplo /me) en lugar de leer un token desde el navegador. Así mantienes el estado de login preciso sin exponer secretos al cliente.

¿Cómo depuro rápidamente si la cookie se está seteando y enviando?

Primero, comprueba si la cookie existe en el navegador tras el login y si persiste tras un hard refresh. Luego inspecciona una petición autenticada en la pestaña Network y confirma que el header Cookie está presente. Si la cookie está en el almacenamiento pero no se envía, casi siempre es por SameSite, Secure, Domain, Path o porque la petición va a un host distinto del que piensas.

El auth de mi prototipo generado por IA es inestable en producción—¿qué debería hacer?

Si heredaste una app generada por IA, las configuraciones de cookies y autenticación suelen ser inconsistentes entre local y producción, sobre todo con subdominios, callbacks de OAuth y proxies. El camino más rápido es una auditoría enfocada del flujo de login real en tu dominio en vivo para localizar el atributo o el paso de redirección que elimina la sesión. Si quieres, FixMyMess puede revisar el código y reparar la configuración de auth y cookies para que funcione en producción, a menudo en 48–72 horas.