Cookies en el panel: los atributos que deciden si viajan
La tabla de cookies con todas sus columnas, qué hace cada atributo, por qué una cookie no llega a su destino, y el particionado por sitio de primer nivel.
Una cookie parece un par de clave y valor y en realidad es un pequeño conjunto de reglas que deciden cuándo se envía, a quién, desde dónde y quién puede leerla. La mayoría de los bugs de autenticación que se atribuyen al servidor son en realidad una cookie que no viaja porque uno de esos atributos lo impide. El panel muestra la tabla entera con todas las columnas, y saber leer esa tabla es lo que convierte “no me deja entrar” en “falta el atributo de sitio cruzado en el entorno de preproducción”.
- Leer todas las columnas de la tabla de cookies y saber qué controla cada una.
- Diagnosticar por qué una cookie no se envía en una petición concreta.
- Explicar los valores del atributo de sitio y cuál corresponde a cada caso.
- Entender el particionado y qué cambia para las cookies de terceros.
La tabla y sus columnas
La sección de cookies del panel de aplicación lista, por origen, todas las cookies con estas columnas.
Nombre y valor. El par en sí. Editable con doble clic, lo que permite reproducir estados de sesión ajenos.
Dominio. A qué dominio se enviará. Un punto inicial implica que también se envía a los subdominios. Una cookie con dominio del dominio padre se envía a todos sus subdominios; una sin ese atributo solo al host exacto que la puso.
Ruta. El prefijo de ruta a partir del cual se envía. Casi siempre la raíz, y cuando no lo es, suele ser el origen de un bug.
Expiración. Cuándo caduca. Una cookie sin este atributo es de sesión y muere al cerrar el navegador, con una excepción importante: los navegadores que restauran pestañas al arrancar restauran también las cookies de sesión, así que “muere al cerrar” no es una garantía.
Tamaño. El total en bytes. Importa más de lo que parece porque las cookies viajan en cada petición al dominio, incluidas las de imágenes y scripts.
Solo HTTP. Si está marcada, el código de la página no puede leerla. Es la defensa principal contra el robo de cookies de sesión mediante inyección de scripts.
Segura. Solo se envía por conexiones cifradas.
Sitio. El atributo que decide el comportamiento en contextos de terceros. Tiene sección propia más abajo.
Clave de partición. El sitio de primer nivel bajo el que está particionada, si lo está.
Prioridad. Una pista sobre cuáles descartar primero cuando se supera el límite por dominio.
Hay un límite por dominio en el número de cookies y en el tamaño total. Al superarlo, el navegador descarta cookies silenciosamente, y el criterio de descarte no es el que a ti te convendría. Es la causa de un bug especialmente cruel: un sitio con muchas cookies de terceros que pierde la de sesión sin ningún error, de forma intermitente y solo para algunos usuarios.
El atributo de sitio
Es el atributo que más bugs produce desde que los navegadores endurecieron su comportamiento por defecto.
Estricto. La cookie solo se envía cuando la navegación se origina en el mismo sitio. Si el usuario llega desde un enlace externo, no viaja. Eso produce el efecto clásico de llegar a una aplicación desde un correo y aparecer sin sesión, incluso teniéndola.
Laxo. Se envía en navegaciones de nivel superior con métodos seguros, y no en peticiones de subrecursos ni en envíos de formulario desde otro sitio. Es el valor por defecto en los navegadores modernos cuando no se especifica ninguno, y es el comportamiento razonable para una cookie de sesión.
Ninguno. Se envía siempre, incluidas las peticiones desde otros sitios. Exige el atributo de seguridad, y una cookie con este valor sin marcar como segura es rechazada por completo.
El diagnóstico más útil de todo el panel se hace con esta columna y una regla: si una cookie no está llegando a una petición desde otro sitio, mira este atributo antes que nada.
Por qué una cookie no viaja
Ocho comprobaciones en orden de frecuencia.
El dominio no coincide. La cookie se puso en un host y se pide desde otro, o desde un subdominio no cubierto.
La ruta no coincide. La cookie tiene una ruta más específica que la de la petición.
Es de sitio estricto o laxo y la petición es de otro sitio. El caso dominante.
Está marcada como segura y la conexión no lo es. Habitual en desarrollo local sin certificado.
Caducó. Comprueba la columna de expiración y el reloj del sistema.
La petición no pide credenciales. Una llamada con fetch a otro origen no envía cookies por defecto. Hay que pedirlo explícitamente con la opción de credenciales, y además el servidor tiene que autorizarlo con la cabecera correspondiente.
El servidor la rechazó al ponerla. El navegador puede descartar una cabecera de cookie mal formada sin decir nada visible en la página. El panel de incidencias sí lo reporta, con el motivo exacto.
Está particionada y el contexto de primer nivel es otro. El caso nuevo, que merece su propia sección.
// Diagnostico de cookies: que hay, que ocupa y que no puedes leer
(() => {
const legibles = document.cookie
? document.cookie.split('; ').map(par => {
const i = par.indexOf('=');
const nombre = par.slice(0, i), valor = par.slice(i + 1);
return { nombre, bytes: par.length, valor: valor.slice(0, 40) };
})
: [];
console.log('Cookies legibles desde JavaScript:', legibles.length);
console.table(legibles.sort((a, b) => b.bytes - a.bytes));
const total = legibles.reduce((s, c) => s + c.bytes, 0);
console.log('Bytes que viajan en cada peticion, al menos:', total);
if (total > 4000) console.warn('Sobrepeso de cookies: cada peticion carga con esto.');
console.log('Las marcadas como solo HTTP no aparecen aqui.');
console.log('Compara este numero con el de la tabla del panel de aplicacion:');
console.log('la diferencia son exactamente las cookies protegidas.');
// Comprobacion de si una peticion concreta llevaria cookies
window.probarCredenciales = async (url) => {
const conCred = await fetch(url, { credentials: 'include' }).catch(e => e);
const sinCred = await fetch(url, { credentials: 'omit' }).catch(e => e);
console.log('Con credenciales:', conCred.status ?? conCred.message);
console.log('Sin credenciales: ', sinCred.status ?? sinCred.message);
console.log('Si difieren, la autenticacion depende de la cookie.');
};
})();
La comparación entre lo que el código puede leer y lo que muestra el panel es un diagnóstico en sí misma: la diferencia son las cookies protegidas contra lectura por script, y comprobar que la de sesión está entre ellas es una verificación de seguridad que cuesta cinco segundos.
Particionado
El cambio más importante de los últimos años en este terreno es que las cookies de terceros dejaron de ser una única bolsa compartida.
Con el particionado, una cookie que un servicio incrustado pone cuando se usa dentro de un sitio no es la misma que la que pone cuando se usa dentro de otro. Cada combinación de sitio de primer nivel y origen incrustado tiene su propio almacén. El atributo que lo activa es Partitioned, y la columna de clave de partición del panel muestra bajo qué sitio de primer nivel está guardada cada una.
Esto tiene dos consecuencias prácticas que se notan al depurar.
Un servicio incrustado ya no puede seguir a un usuario entre sitios con cookies, que es exactamente el objetivo del cambio.
Un servicio incrustado legítimo que necesitaba estado —un chat de soporte, un reproductor con preferencias, una pasarela de pago en un marco— tiene que declarar el particionado explícitamente, y hasta que lo haga, su estado desaparece. Muchos bugs de “el widget no recuerda nada” son exactamente esto, y el panel los diagnostica de un vistazo mirando si la cookie tiene clave de partición o si directamente no aparece.
Hay un coste de las cookies que ninguna auditoría de rendimiento señala y que en sitios grandes es sorprendentemente alto: viajan en la cabecera de cada petición al dominio, sin excepción y sin compresión útil. Cada imagen, cada script, cada hoja de estilos, cada llamada de API que vaya a un dominio con cookies arrastra todas las que apliquen. Con cuatro kilobytes de cookies —que es una cifra normal en un sitio con analítica, personalización y pruebas A/B— y cincuenta peticiones al mismo dominio, son doscientos kilobytes de subida por carga de página. Y la subida es el sentido lento de casi todas las conexiones domésticas y móviles, con anchos de banda que son una fracción de los de bajada. Peor aún: una petición cuya cabecera no cabe en el primer paquete cuesta un viaje de ida y vuelta adicional, así que unas cookies grandes pueden añadir latencia real a cada petición, no solo bytes. Esto no aparece en ningún informe porque las herramientas de rendimiento miden lo que baja, y las cabeceras de petición no se cuentan en ninguna métrica de peso. Las tres consecuencias de diseño son concretas y merecen aplicarse desde el principio de un proyecto. Una: sirve los recursos estáticos desde un dominio sin cookies. Un dominio distinto para imágenes, scripts y estilos elimina el impuesto entero para la mayoría de las peticiones; el coste de una conexión adicional se amortiza en la primera decena de recursos. Dos: no uses cookies para datos que el servidor no necesita en cada petición. Una preferencia de interfaz, un estado de tutorial, un identificador de prueba A/B que solo se lee en el cliente: nada de eso debería estar en una cookie, porque una cookie viaja siempre y el almacenamiento web no viaja nunca. La costumbre de usar cookies como almacén general es un residuo de la época en que no había alternativa. Tres: audita el tamaño total periódicamente, porque crece solo: cada herramienta nueva añade la suya, nadie quita las viejas, y el día en que se supera el límite del dominio empiezan a desaparecer cookies al azar, incluida la de sesión. Esa es la forma en que un problema de rendimiento se convierte en un problema de autenticación intermitente que nadie sabe explicar.