Por Hugo Cayón Laso
Cookies, localStorage y sessionStorage: diferencias, usos y cuándo elegir cada uno
El navegador ofrece tres formas de guardar datos en el cliente: cookies, localStorage y sessionStorage. Los tres hacen algo parecido a simple vista, pero tienen comportamientos, límites y implicaciones de seguridad muy distintos.
Elegir el equivocado puede ser un bug difícil de encontrar, o peor, una vulnerabilidad de seguridad.
Cookies
Las cookies son el mecanismo más antiguo y el más potente, aunque también el más complejo. Se inventaron en 1994 y siguen siendo la base de la autenticación en la mayoría de las webs.
Cómo funcionan: el servidor puede establecer una cookie en la respuesta HTTP. El navegador la guarda y la envía automáticamente en cada petición posterior al mismo dominio. El servidor también puede leerla y modificarla.
HTTP/1.1 200 OK
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax; Max-Age=86400
Lo que las hace únicas:
- Las envía el navegador automáticamente en cada petición. No tienes que hacer nada en JavaScript.
- El servidor puede crearlas y leerlas, no solo el cliente.
- Tienen fecha de expiración configurable.
- Se pueden restringir a un dominio y a una ruta específica.
Capacidad: ~4 KB por cookie.
Cuándo usarlas: sesiones de usuario, autenticación, preferencias que el servidor necesita conocer.
Atributos de seguridad importantes
Set-Cookie: token=xyz;
HttpOnly; /* JavaScript no puede leerla */
Secure; /* Solo se envía por HTTPS */
SameSite=Lax /* Protección contra CSRF */
HttpOnly: impide que JavaScript acceda a la cookie condocument.cookie. Protege contra XSS.Secure: la cookie solo viaja por HTTPS, nunca por HTTP.SameSite: controla cuándo se envía la cookie en peticiones cross-site.Laxes el equilibrio razonable;Strictes más seguro pero puede romper flujos de OAuth.
localStorage
localStorage es una API de JavaScript para guardar datos en el navegador de forma persistente. Los datos no tienen fecha de expiración y no se borran al cerrar el navegador ni el sistema.
// Guardar
localStorage.setItem('tema', 'oscuro');
localStorage.setItem('usuario', JSON.stringify({ nombre: 'Hugo', rol: 'admin' }));
// Leer
const tema = localStorage.getItem('tema');
const usuario = JSON.parse(localStorage.getItem('usuario'));
// Borrar
localStorage.removeItem('tema');
localStorage.clear(); // borra todo
Características:
- Persiste indefinidamente hasta que se borra explícitamente.
- Solo accesible desde JavaScript (el servidor no lo ve).
- No se envía al servidor en cada petición.
- Compartido entre todas las pestañas del mismo origen.
Capacidad: ~5-10 MB según el navegador.
Cuándo usarlo: preferencias de usuario (tema claro/oscuro), borradores guardados localmente, datos de la app que no necesita el servidor.
El error más común con localStorage
Guardar tokens de autenticación en localStorage es una mala práctica extendida:
// MAL: el token queda expuesto a cualquier script de la página
localStorage.setItem('auth_token', token);
Cualquier script de terceros (analytics, widgets, librerías comprometidas) puede leer localStorage. Si hay una vulnerabilidad XSS, el atacante roba el token con una línea de JavaScript. Una cookie HttpOnly no tiene ese problema porque JavaScript directamente no puede acceder a ella.
sessionStorage
sessionStorage funciona igual que localStorage en la API, con una diferencia clave: los datos se borran cuando se cierra la pestaña.
sessionStorage.setItem('paso_actual', '2');
const paso = sessionStorage.getItem('paso_actual');
sessionStorage.removeItem('paso_actual');
Características:
- Se borra al cerrar la pestaña (no la ventana del navegador completa).
- No se comparte entre pestañas, aunque sean del mismo origen.
- Solo accesible desde JavaScript.
Capacidad: ~5-10 MB.
Cuándo usarlo: estado temporal de un formulario multipaso, datos de una sesión de compra que no deben persistir, cualquier cosa que solo tiene sentido mientras el usuario está en esa pestaña concreta.
Comparativa directa
| Cookies | localStorage | sessionStorage | |
|---|---|---|---|
| Capacidad | ~4 KB | ~5-10 MB | ~5-10 MB |
| Persistencia | Configurable | Permanente | Solo la pestaña |
| Acceso servidor | Sí (automático) | No | No |
| Acceso JS | Sí (salvo HttpOnly) | Sí | Sí |
| Compartido entre pestañas | Sí | Sí | No |
| Se envía en peticiones | Sí | No | No |
Guía de decisión rápida
¿El servidor necesita los datos? → Cookie.
¿Son datos de autenticación?
→ Cookie con HttpOnly y Secure. Nunca localStorage.
¿Son preferencias del usuario que deben persistir entre sesiones?
→ localStorage.
¿Son datos temporales que solo tienen sentido en esta pestaña?
→ sessionStorage.
¿Necesitas más de 4 KB en el cliente y el servidor no los necesita?
→ localStorage o sessionStorage.
Conclusión
Las tres opciones existen porque resuelven problemas distintos. La elección incorrecta no siempre rompe la funcionalidad de inmediato, pero puede crear problemas de seguridad o de experiencia de usuario que son difíciles de diagnosticar.
La regla más importante: nunca guardes tokens de sesión o credenciales en localStorage. Para eso están las cookies con los atributos de seguridad adecuados.
Comentarios
Mi primer experimento creando una app para iOS y macOS
Menta: Por qué creé mi propio gestor de tiempo para el trabajo
Posts relacionados
- 18 de mayo de 20264 min