Ba nơi lưu dữ liệu ở client, khác nhau ở ai đọc được, tồn tại bao lâu, và có gửi lên server không.
| Cookie | localStorage | sessionStorage | |
|---|---|---|---|
| Dung lượng | ~4KB | ~5–10MB | ~5–10MB |
| Gửi kèm mọi request | có | không | không |
| JS đọc được | có, trừ khi HttpOnly | có | có |
| Hết hạn | tự đặt được | không bao giờ | đóng tab là mất |
| Phạm vi | theo domain + path | theo origin | theo từng tab |
localStorage.setItem('theme', 'dark')
localStorage.getItem('theme')
sessionStorage.setItem('draft', text) // tab khác không thấy
document.cookie = 'lang=vi; Max-Age=31536000; Path=/; SameSite=Lax'Về bảo mật — điểm quan trọng nhất: token phiên nên nằm trong cookie HttpOnly + Secure + SameSite, không nằm trong localStorage.
Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Lax; Path=/Lý do: localStorage JavaScript nào cũng đọc được. Chỉ cần một lỗ XSS — hoặc một thư viện npm bị chèn mã độc — là token bị lấy đi. Cookie HttpOnly thì JS không đọc được, nên XSS không lấy trực tiếp được.
Đánh đổi: cookie tự gửi kèm mọi request nên mở ra rủi ro CSRF — bù lại bằng SameSite=Lax/Strict và token chống CSRF.
Chốt: token đăng nhập → cookie HttpOnly. Tuỳ chọn giao diện (theme, ngôn ngữ), cache không nhạy cảm → localStorage. Dữ liệu chỉ có nghĩa trong một tab (bản nháp, bước của wizard) → sessionStorage.