Bỏ qua đến nội dung
KhaiziNam Blog KhaiziNam Blog
Quay lại
Read in English

Session vs JWT: Developer Nên Chọn Cái Nào? So Sánh Thực Tế 2026

Phân tích chi tiết Session và JWT theo 6 tiêu chí kỹ thuật thực tế - kiến trúc, revocation, scaling, hiệu năng, bảo mật và độ phức tạp - giúp developer đưa ra quyết định đúng cho từng loại dự án trong năm 2026.

Bạn bắt đầu dự án mới và ngay câu hỏi đầu tiên về authentication đã gây tranh cãi trong team: Session hay JWT? Tìm trên Google thì nửa bài bảo JWT hiện đại hơn, nửa bài bảo Session an toàn hơn - không bài nào cho câu trả lời thẳng vào bài toán cụ thể của bạn. Sự thật là không có cái nào “tốt hơn” tuyệt đối. Chỉ có cái nào phù hợp hơn với yêu cầu kỹ thuật của từng dự án. Bài viết này so sánh Session và JWT theo từng tiêu chí có thể đo được, kèm ví dụ code và case study thực tế, để bạn tự quyết định đúng thay vì làm theo trend.

Nội dung bài viết:


1. Bản chất khác biệt cốt lõi giữa Session và JWT

1.1 Session — Stateful, server nắm quyền kiểm soát

Session là cơ chế stateful: sau khi user đăng nhập thành công, server tạo một session object lưu trữ trong bộ nhớ hoặc một store bên ngoài (Redis, database), rồi gửi về client một session ID ngắn gọn dạng cookie. Mỗi request tiếp theo, client đính kèm cookie đó, server tra cứu session store để lấy thông tin user. Toàn bộ “trạng thái” nằm ở phía server — client chỉ cầm một cái chìa khóa không chứa thông tin gì.

// Node.js Express + Redis Session Store const session = require(‘express-session’); const RedisStore = require(‘connect-redis’).default;

app.use(session({ store: new RedisStore({ client: redisClient }), secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, cookie: { httpOnly: true, secure: true, // Chỉ gửi qua HTTPS sameSite: ‘strict’, // Chặn CSRF maxAge: 7 * 24 * 60 * 60 * 1000 // 7 ngày } }));

// Đăng nhập — lưu userId vào session app.post(‘/login’, async (req, res) => { const user = await verifyCredentials(req.body); req.session.userId = user.id; req.session.role = user.role; res.json({ message: ‘Logged in’ }); });

// Logout — destroy session tức thì app.post(‘/logout’, (req, res) => { req.session.destroy(); res.clearCookie(‘connect.sid’); res.json({ message: ‘Logged out’ }); });

1.2 JWT — Stateless, client mang theo mọi thứ

JWT là cơ chế stateless: sau khi đăng nhập, server tạo một token chứa đầy đủ thông tin cần thiết (userId, role, exp…), ký bằng secret key, trả về client. Mỗi request, client gửi token trong Authorization header. Server chỉ cần verify chữ ký là xong — không tra database, không cần shared store. Toàn bộ “trạng thái” nằm trong chính token.

// Node.js Express + JWT const jwt = require(‘jsonwebtoken’); const crypto = require(‘crypto’);

// Đăng nhập — cấp access token + refresh token app.post(‘/login’, async (req, res) => { const user = await verifyCredentials(req.body);

const accessToken = jwt.sign( { sub: user.id, role: user.role }, process.env.JWT_SECRET, { algorithm: ‘HS256’, expiresIn: ‘15m’, jwtid: crypto.randomUUID() } );

const refreshToken = crypto.randomBytes(40).toString(‘hex’); await db.refreshTokens.create({ token: hash(refreshToken), userId: user.id });

res.cookie(‘refreshToken’, refreshToken, { httpOnly: true, secure: true, sameSite: ‘strict’, maxAge: 30 * 24 * 60 * 60 * 1000 }); res.json({ accessToken }); });

// Middleware verify function requireAuth(req, res, next) { const token = req.headers.authorization?.split(’ ’)[1]; if (!token) return res.status(401).json({ error: ‘Unauthorized’ }); try { req.user = jwt.verify(token, process.env.JWT_SECRET, { algorithms: [‘HS256’] }); next(); } catch { res.status(401).json({ error: ‘Invalid or expired token’ }); } }

2. Before/After: Chọn sai cơ chế xác thực gây ra vấn đề gì?

2.1 Dùng in-memory Session cho hệ thống scale ngang — hậu quả thực tế

Một startup logistics xây API với Express và dùng session lưu trong memory (mặc định của express-session, không cấu hình store). Khi deploy lên 3 server phía sau load balancer, user liên tục bị đăng xuất ngẫu nhiên vì mỗi request có thể đến một server khác không có session đó. Họ phải bật sticky session trên load balancer — giải pháp vá víu khiến một server quá tải trong khi hai server còn lại rảnh. Phải mất 2 sprint để migrate sang JWT và giải quyết triệt để.

2.2 Dùng JWT TTL dài cho hệ thống cần kiểm soát chặt — rủi ro bảo mật

Một SaaS B2B cấp JWT access token TTL 24 giờ vì ngại xây refresh token flow. Khi phát hiện một tài khoản enterprise bị compromise, admin cần revoke ngay nhưng không có cơ chế nào — token vẫn hợp lệ trong 24 giờ tiếp theo. Phải emergency-deploy một blacklist service trong đêm. Sau sự cố, họ rebuild lại toàn bộ auth flow với access token 15 phút + refresh token rotation.

3. So sánh 6 tiêu chí kỹ thuật thực tế

3.1 Revocation — Thu hồi quyền truy cập

Session thắng rõ ràng. Xóa session ID khỏi Redis là user bị kick ngay lập tức, không có độ trễ. Phù hợp cho mọi bài toán cần kiểm soát chặt: ban tài khoản, đổi password buộc đăng xuất, admin force logout.

JWT phức tạp hơn. Không có built-in revocation — token hợp lệ cho đến khi hết hạn. Giải pháp: blacklist jti trong Redis hoặc chấp nhận độ trễ tối đa bằng TTL của access token (15 phút). Xem cách implement chi tiết tại bài JWT Security Best Practices 2026.

3.2 Horizontal Scaling — Scale ngang

JWT thắng rõ ràng. Stateless hoàn toàn — bất kỳ server nào cũng verify được token chỉ cần có secret/public key. Không phụ thuộc shared store, không sticky session, scale không giới hạn.

Session cần thêm infrastructure. Phải dùng Redis hoặc database-backed session store để share session giữa các server. Redis thêm một dependency, thêm một điểm có thể fail, thêm latency ~0.1–1ms mỗi request.

3.3 Performance — Hiệu năng

JWT nhanh hơn ở high-throughput. Verify signature là phép toán CPU thuần (HMAC ~microseconds), không có network round-trip. Tại 10.000 req/s, JWT tiết kiệm 10.000 Redis reads/s — đáng kể ở scale lớn.

Session thêm latency nhỏ nhưng ổn định. Redis lookup ~0.1–1ms nếu cùng datacenter — thường không phải bottleneck. Nhưng nếu Redis bị tải cao hoặc network latency tăng, mọi request đều bị ảnh hưởng.

3.4 Security — Bảo mật

Session: attack surface nhỏ hơn mặc định. Session ID ngắn, không chứa thông tin. Rủi ro chính là CSRF (khắc phục bằng SameSite=Strict) và session fixation (khắc phục bằng regenerate session ID sau login).

JWT: attack surface rộng hơn nếu implement sai. Algorithm confusion, weak secret, XSS qua localStorage, payload exposure — mỗi điểm đều là lỗ hổng tiềm năng. Nhưng nếu implement đúng theo best practices, bảo mật tương đương Session. Tham khảo danh sách đầy đủ tại bài JWT Security Best Practices.

3.5 Complexity — Độ phức tạp triển khai

Session đơn giản hơn đáng kể. Framework hỗ trợ sẵn: Laravel session, Express session, Django session. Cấu hình vài dòng, ít footgun, ít thứ có thể implement sai.

JWT cần tự xây nhiều hơn. Refresh token flow, rotation logic, storage strategy, revocation mechanism — mỗi phần đều có cạm bẫy. Phù hợp developer đã hiểu rõ các rủi ro và sẵn sàng đầu tư thời gian implement đúng.

3.6 Cross-domain & Mobile — Đa nền tảng

JWT linh hoạt hơn. Authorization header hoạt động tự nhiên với mọi client: browser, mobile app, CLI tool, third-party service. Cookie có giới hạn cross-domain phức tạp hơn (CORS, SameSite restrictions).

Session phụ thuộc cookie. Mobile app (React Native, native iOS/Android) không có cookie mechanism như browser — phải implement thêm lớp cookie handling. Không tự nhiên bằng JWT.

4. Khi nào chọn Session, khi nào chọn JWT?

4.1 Chọn Session khi dự án có những đặc điểm sau
4.2 Chọn JWT khi dự án có những đặc điểm sau

Nếu bạn đang chuẩn bị phỏng vấn và cần biết cách trả lời câu hỏi “Session vs JWT” trước interviewer, xem thêm Session vs JWT Phỏng Vấn: Hướng Dẫn Đầy Đủ Để Không Bao Giờ Bị Hỏi Khó.

5. Hybrid Approach — Dùng cả hai có hợp lý không?

5.1 Khi nào Hybrid là lựa chọn đúng?

Nhiều hệ thống production thực tế dùng cả hai — không phải vì thiếu quyết đoán mà vì mỗi phần của hệ thống có yêu cầu khác nhau. Hybrid hợp lý khi bạn có nhiều loại client với nhu cầu authentication khác nhau.

5.2 Case study: E-commerce platform 80.000 DAU

Một sàn thương mại điện tử tại Việt Nam với 80.000 DAU triển khai hybrid authentication sau 18 tháng vận hành:

Kết quả sau 12 tháng hybrid: zero authentication-related security incident, team 4 backend developer maintain được toàn bộ, chi phí Redis cho session ~$20/tháng — không đáng kể. Điều quan trọng: mỗi phần được document rõ lý do chọn cơ chế nào, tránh để sau này team mới không hiểu tại sao hệ thống lại “lộn xộn”.

6. 6 sai lầm phổ biến khi chọn cơ chế xác thực

  1. Chọn JWT vì “nó hiện đại hơn” hoặc “mọi người đang dùng” → Fix: quyết định dựa trên yêu cầu kỹ thuật cụ thể — kiến trúc, client type, revocation requirement. JWT phức tạp hơn Session nếu implement đúng chuẩn.
  2. Dùng express-session mặc định (in-memory) cho production → Fix: luôn configure Redis store hoặc database store ngay từ đầu, dù ban đầu chỉ có 1 server. Thói quen tốt tránh được khủng hoảng sau này.
  3. Đặt JWT access token TTL dài (8h, 24h) vì ngại xây refresh token → Fix: đầu tư implement đúng một lần — access token 15 phút + refresh token rotation. Tham khảo code mẫu tại bài JWT Security Best Practices.
  4. Lưu JWT trong localStorage → Fix: access token in-memory (biến JS), refresh token trong httpOnly cookie. Đây là điểm hay bị bỏ qua nhất trong các dự án fresher.
  5. Không có cơ chế “revoke tất cả session” khi dùng JWT → Fix: lưu refresh token trong DB với userId. Khi cần revoke toàn bộ (đổi password, phát hiện breach), xóa hết record theo userId là đủ.
  6. Dùng hybrid mà không document lý do → Fix: mỗi service/module dùng cơ chế nào phải có ADR (Architecture Decision Record) giải thích tại sao. Tránh “legacy confusion” khi team mới join.

7. FAQ — Câu hỏi thường gặp về Session vs JWT

7.1 JWT có thể replace hoàn toàn Session không?

Về mặt kỹ thuật có, nhưng không phải lúc nào cũng nên. JWT replace Session tốt nhất trong REST API và microservices. Với web app truyền thống cần revoke tức thì và đơn giản hóa codebase, Session vẫn là lựa chọn hợp lý hơn. “Replace hoàn toàn” là tư duy sai — nên là “chọn đúng công cụ cho đúng bài toán”.

7.2 Nếu dùng Next.js thì nên chọn cái nào?

Phụ thuộc vào rendering strategy. Next.js với App Router + Server Components: Session hoặc NextAuth.js với JWT adapter đều ổn. Next.js như một BFF (Backend for Frontend) trả về JSON cho mobile app: JWT tự nhiên hơn. Nhiều production Next.js app dùng NextAuth.js — thư viện này hỗ trợ cả database session và JWT strategy, có thể switch giữa hai cái tùy môi trường.

7.3 Session có thể dùng cho mobile app không?

Có nhưng cần effort thêm. Mobile app phải tự implement cookie jar (lưu và gửi cookie như browser). Với React Native, có thể dùng thư viện như react-native-cookies. Tuy nhiên effort này thường không đáng — JWT là lựa chọn tự nhiên hơn cho mobile, cả về implementation lẫn ecosystem hỗ trợ.

7.4 Redis bị down thì Session authentication có bị ảnh hưởng không?

Có — đây là điểm yếu của Session store tập trung. Nếu Redis down, toàn bộ session lookup fail và user bị kick. Giải pháp: Redis Sentinel hoặc Redis Cluster cho high availability, circuit breaker để fallback gracefully, và monitoring alert khi Redis latency tăng bất thường. JWT không có vấn đề này — nhưng JWT cũng mất đi khả năng revoke ngay lập tức.

7.5 Ở vị trí fresher/junior, nên học cái nào trước?

Học Session trước — ít concept phức tạp hơn, framework hỗ trợ sẵn, dễ debug, dễ hiểu flow. Sau khi nắm vững Session, học JWT sẽ dễ hơn vì bạn đã hiểu authentication flow cơ bản. Thứ tự học: Session → JWT basics → JWT security → Refresh token flow → Hybrid architecture. Nếu bạn đang chuẩn bị phỏng vấn, bài Câu hỏi phỏng vấn JWT thường gặp sẽ giúp bạn nắm chắc phần JWT trước buổi phỏng vấn.

Tổng kết và bước tiếp theo

Session và JWT không phải đối thủ — chúng là hai công cụ giải hai bài toán khác nhau. Session phù hợp khi bạn cần kiểm soát chặt, revoke tức thì, và muốn đơn giản hóa codebase trong kiến trúc monolith. JWT phù hợp khi bạn cần stateless, scale ngang, và phục vụ nhiều loại client trong kiến trúc distributed. Quyết định đúng không đến từ việc theo trend — mà từ việc đọc yêu cầu kỹ thuật của dự án và chọn công cụ phù hợp. Bước tiếp theo: review lại hệ thống hiện tại của bạn, xác định đang thuộc use case nào trong bài này, và kiểm tra xem implementation hiện tại có đang theo đúng best practices không.

Tác giả: Nguyễn Hữu Khải

21/04/2026

Xem thêm: 


Chia sẻ bài viết:

Bài viết liên quan

Mức lương fresher junior IT 2026 - PHP, NodeJS, React, Flutter thực tế

Bảng lương thực tế của fresher và junior IT Việt Nam năm 2026 theo từng stack - PHP/Laravel, Node.js, ReactJS, Flutter - kèm cách đọc con số để biết bạn đang ở đâu trên thị trường, dù bạn chưa đi làm hay đã đi làm 1-2 năm.

Portfolio cho dev junior - cần có gì để được chú ý

Portfolio developer junior là tập hợp các project thực tế, kỹ năng và thông tin nghề nghiệp mà bạn trình bày để nhà tuyển dụng đánh giá năng lực - thay thế cho phần kinh nghiệm làm việc còn trống trên CV. Với fresher và junior developer, portfolio không phải thứ "có thì tốt" - đó là bằng chứng duy nhất bạn có thể đưa ra để chứng minh bạn thực sự biết làm việc, không chỉ biết lý thuyết.

Cover Letter Fresher IT: Cách Viết Để Được Gọi Phỏng Vấn

Cách viết cover letter cho fresher IT khi chưa có kinh nghiệm, kèm mẫu thực tế, cấu trúc chuẩn và mẹo tăng tỷ lệ vượt qua vòng lọc CV.

Ngôn ngữ cơ thể trong phỏng vấn — những điều vô tình mất điểm

Ngôn ngữ cơ thể phỏng vấn IT là tập hợp những tín hiệu phi ngôn ngữ — tư thế, ánh mắt, cử chỉ tay, giọng điệu — mà nhà tuyển dụng quan sát song song với câu trả lời kỹ thuật của bạn. Hiểu và kiểm soát tốt những tín hiệu này giúp bạn tạo ấn tượng chuyên nghiệp ngay từ những giây đầu tiên bước vào phòng phỏng vấn.

Cách đặt câu hỏi ngược khi phỏng vấn IT — đừng chỉ hỏi lương

Hướng dẫn cách đặt câu hỏi ngược khi phỏng vấn IT dành cho fresher và junior developer — tại sao phần "bạn có câu hỏi gì không" quan trọng hơn bạn nghĩ, 20+ câu hỏi thực tế phân loại theo mục đích, những câu tuyệt đối không nên hỏi, và cách chọn đúng câu hỏi theo từng vòng phỏng vấn.

Vì sao bạn chọn ngành IT — câu trả lời tạo ấn tượng với HR khi phỏng vấn

Hướng dẫn cách trả lời câu hỏi "vì sao bạn chọn ngành IT" trong phỏng vấn — phân tích điều HR thực sự muốn nghe, framework xây dựng câu trả lời theo từng hoàn cảnh, script mẫu cho fresher và career changer, cùng các lỗi phổ biến khiến câu trả lời nghe sáo rỗng và không đáng tin.

Điểm yếu lớn nhất của bạn là gì — cách trả lời không bị loại ngay khi phỏng vấn IT

Hướng dẫn cách trả lời câu hỏi "điểm yếu lớn nhất của bạn là gì" trong phỏng vấn IT — phân tích tại sao câu này là bẫy, framework trả lời 3 bước, script mẫu cho fresher và junior developer, cùng danh sách lỗi hay gặp khiến ứng viên bị loại ngay lập tức.

Câu Hỏi Phỏng Vấn ReactJS Junior: 30+ Câu Thực Tế Kèm Đáp Án Chuẩn 2026

Tổng hợp 30+ câu hỏi phỏng vấn ReactJS junior hay gặp nhất năm 2026 — từ Virtual DOM, hooks, state management đến performance optimization — kèm đáp án chi tiết và ví dụ code giúp bạn tự tin vượt qua mọi vòng technical interview.Bạn đã học React được vài tháng, build được project, nhưng mỗi lần vào phỏng vấn lại bị hỏi những thứ không có trong tutorial nào bạn từng đọc? "useEffect cleanup function

Refresh Token Node.js và Laravel: Hướng Dẫn Implement Chuẩn Production 2026

Hướng dẫn implement Refresh Token hoàn chỉnh cho cả Node.js (Express) và Laravel PHP — từ thiết kế database, viết API endpoint, xử lý rotation, đến các lỗi thường gặp — giúp developer xây hệ thống JWT authentication chuẩn production trong năm 2026.

JWT Security Best Practices: Bảo mật JWT đúng cách trong dự án thực tế 2026

Bạn đã biết JWT là gì và dùng được trong dự án, nhưng liệu bạn có đang mắc những lỗi bảo mật mà hacker chỉ cần vài phút để khai thác? Nhiều developer triển khai JWT đúng về mặt chức năng nhưng sai hoàn toàn về bảo mật — lưu token trong localStorage, dùng secret key yếu, không rotate refresh token, hay bỏ qua validate claims.


Bài trước
Refresh Token Node.js và Laravel: Hướng Dẫn Implement Chuẩn Production 2026
Bài tiếp theo
JWT Security Best Practices: Bảo mật JWT đúng cách trong dự án thực tế 2026