2.1 - Bản đồ module: chuyện gì xảy ra khi bấm Đăng nhập
Tóm tắt: Module này có nhiều thuật ngữ rời rạc — DNS, TCP, TLS, HTTP, JSON, JWT, database, hosting — và học từng cái riêng lẻ rất dễ quên. Bài này là bản đồ nối tất cả lại bằng một ví dụ duy nhất chạy xuyên suốt: chuyện gì xảy ra khi bạn gõ
crm.company.comvà bấm Đăng nhập. Tám chặng, mỗi chặng ứng với một bài trong module. Cách học được khuyến nghị: đọc bài này trước, học từng bài, rồi quay lại đọc bài này lần nữa — lần hai bạn sẽ thấy các mảnh ghép vào đúng chỗ. Và thứ tự quan trọng: học từ trải nghiệm hằng ngày tới thuật ngữ kỹ thuật, không phải ngược lại.
Mục tiêu bài học
Sau bài này bạn có thể:
- Mô tả tám chặng của một request web.
- Biết mỗi bài trong module ứng với chặng nào.
- Chọn thứ tự học phù hợp với nền tảng của mình.
- Nhận ra chặng nào gây lỗi khi có sự cố.
Cần biết trước: không cần kiến thức mạng nào. Bạn cần trình duyệt Chrome hoặc Edge và một terminal. Đoạn code ở Bài 1 chạy trên API bạn đã tạo ở bài 0.5 — Web API đầu tiên.
Nội dung bài học
2.1.1 — Ví dụ xuyên suốt
Tám chặng này xảy ra trong khoảng 200 mili giây, và mỗi chặng là một bài trong module.
2.1.2 — Từng chặng, học ở bài nào
Chặng 1–2: gõ URL và phân giải DNS → bài 2.2
crm.company.com -> DNS resolver -> 203.0.113.45Trình duyệt không biết crm.company.com nằm ở đâu. Nó hỏi DNS — giống tra danh bạ điện thoại — để lấy địa chỉ IP.
Chặng 3: kết nối TCP → bài 2.2
Trình duyệt và server bắt tay ba bước để mở một kênh tin cậy. TCP đảm bảo dữ liệu tới đủ và đúng thứ tự.
Chặng 4: bắt tay TLS → bài 2.4
Hai bên trao đổi khoá để mã hoá. Đây là chữ S trong HTTPS. Không có nó, mật khẩu bạn gửi đi ai cũng đọc được trên đường truyền.
Chặng 5: request HTTP → bài 2.4 và bài 2.5
POST /api/auth/login HTTP/1.1
Host: crm.company.com
Content-Type: application/json
{ "email": "[email protected]", "password": "..." }Đây là chỗ bạn sẽ làm việc nhiều nhất với tư cách backend engineer: method, đường dẫn, header, body, và mã trạng thái trả về.
Chặng 6: backend xử lý → bài 2.3
Server nhận request, phân tích, và quyết định làm gì. Đây là mô hình client–server: một bên hỏi, một bên trả lời.
Chặng 7: truy vấn database → bài 2.6
SELECT Id, PasswordHash FROM Users WHERE Email = @email;Backend hỏi database xem tài khoản có tồn tại không và mật khẩu có khớp không.
Chặng 8: trả JWT → bài 2.7
{ "token": "eyJhbGciOiJIUzI1NiIs..." }Từ các request sau, trình duyệt đính token này vào header Authorization để server biết bạn là ai mà không phải đăng nhập lại.
Và tất cả chạy ở đâu? → bài 2.8 và bài 2.9
2.1.3 — Chặng nào gây lỗi gì
Bảng này hữu ích ngay cả khi bạn chưa học hết module — nó giúp khoanh vùng khi có sự cố:
| Triệu chứng | Chặng có vấn đề |
|---|---|
ERR_NAME_NOT_RESOLVED | DNS (chặng 2) |
ERR_CONNECTION_REFUSED | TCP — server không chạy hoặc sai cổng (chặng 3) |
ERR_CERT_AUTHORITY_INVALID | TLS — chứng chỉ sai hoặc hết hạn (chặng 4) |
404 Not Found | HTTP — sai đường dẫn (chặng 5) |
500 Internal Server Error | Backend — lỗi trong code (chặng 6) |
| Timeout khi gọi API | Database chậm hoặc mất kết nối (chặng 7) |
401 Unauthorized | Token thiếu, sai hoặc hết hạn (chặng 8) |
Nguyên tắc chẩn đoán: mã lỗi cho biết chặng nào, và biết chặng nào tiết kiệm rất nhiều thời gian tìm kiếm (bài 2.11).
2.1.4 — Thứ tự học theo nền tảng
Nếu bạn hoàn toàn mới với web: đọc theo đúng thứ tự 2.2 → 2.9. Mỗi bài dựa trên bài trước.
Nếu đã biết HTML/CSS/JavaScript: bắt đầu từ 2.4 (HTTP) và 2.5 (API và JSON) — hai bài gần với công việc backend nhất. Quay lại 2.2 và 2.3 khi cần.
Nếu đã làm frontend: 2.6 (database) và 2.7 (xác thực) là hai mảng bạn ít tiếp xúc nhất. Ưu tiên chúng.
Nếu chỉ có 2 giờ: 2.4 và 2.7. Hai bài này chiếm phần lớn công việc hàng ngày của một backend engineer.
2.1.5 — Học từ trải nghiệm tới thuật ngữ
Cách học không hiệu quả:
Học thuộc: "DNS là hệ thống phân giải tên miền thành địa chỉ IP"
-> Nhớ được vài ngày, rồi quênCách học hiệu quả:
1. Mở terminal, gõ: nslookup example.com
2. Thấy kết quả trả về một vài địa chỉ IP
3. Gõ một địa chỉ IP đó vào trình duyệt
-> thường KHÔNG ra trang example.com: báo lỗi 403 hoặc cảnh báo chứng chỉ
4. "À, DNS chỉ đưa mình tới đúng máy. Chọn trang nào trên máy đó là chuyện khác"
-> Hiểu, và không quênBước 3 không ra trang như bạn tưởng, và chính chỗ bất ngờ đó làm bạn nhớ lâu. Bài 2 bên dưới giải thích vì sao.
Với mỗi khái niệm trong module, tìm một cách tự tay thử nó. Vài lệnh đáng biết ngay:
nslookup crm.company.com # DNS trả về IP nào
curl -i https://example.com # Xem đầy đủ header phản hồi
ping google.com # Đo độ trễ mạngVà công cụ quan trọng nhất: DevTools của trình duyệt, tab Network. Mọi thứ trong module này đều quan sát được ở đó — request, header, mã trạng thái, thời gian từng chặng.
2.1.6 — Những gì cần đạt sau module
Danh sách rà soát trước khi sang Module 3
- Mô tả được tám chặng của một request web.
- Biết mã lỗi nào ứng với chặng nào.
- Đã tự chạy nslookup và curl ít nhất một lần.
- Mở được DevTools tab Network và đọc được một request.
- Phân biệt được HTTP và HTTPS khác nhau ở đâu.
- Giải thích được JWT dùng để làm gì.
- Biết database nằm ở chặng nào trong luồng.
Bài tập áp dụng
Bài 1 — Lần theo luồng thật
Mở DevTools trên một trang có đăng nhập, thực hiện đăng nhập, và xác định từng chặng trong tab Network.
Tiêu chí hoàn thành: bạn ánh xạ được tám chặng vào những gì DevTools hiện ra, và nhận ra có chặng không hiện ra ở đó.
Gợi ý và lời giải — Bài 1
Gợi ý. Bấm vào một request rồi mở tab Timing. Nó chia sẵn thời gian theo từng giai đoạn.
Lời giải — đọc bảng Timing của request đăng nhập (số liệu minh họa, máy và mạng của bạn sẽ cho số khác):
POST https://crm.company.com/api/auth/login
Queueing 0,42 ms
Stalled 1,18 ms
DNS Lookup 24,00 ms <- chặng 2
Initial connection 48,00 ms <- chặng 3 (TCP)
SSL 31,00 ms <- chặng 4 (TLS, nằm trong Initial connection)
Request sent 0,21 ms <- chặng 5
Waiting (TTFB) 186,00 ms <- chặng 6 và 7
Content Download 12,00 ms <- chặng 8
---------
Total 271,81 msÁnh xạ tám chặng:
| Chặng | Trong DevTools | Đo được |
|---|---|---|
| 1. Gõ URL | Queueing, Stalled | 1,6 ms |
| 2. DNS | DNS Lookup | 24 ms |
| 3. TCP | Initial connection trừ SSL | 17 ms |
| 4. TLS | SSL | 31 ms |
| 5. HTTP request | Request sent | 0,2 ms |
| 6. Backend xử lý | Waiting (TTFB) | 186 ms |
| 7. Database | cũng nằm trong Waiting | không tách được |
| 8. Trả JWT | Content Download | 12 ms |
Và đây là điều bài tập muốn bạn thấy: chặng 7 KHÔNG hiện ra riêng.
DevTools chỉ thấy tới biên của máy chủ.
Nó biết "máy chủ mất 186 ms", nhưng không biết trong 186 ms đó:
- bao nhiêu cho middleware xác thực
- bao nhiêu cho truy vấn database
- bao nhiêu cho việc băm mật khẩuBăm mật khẩu đáng chú ý: BCrypt hoặc Argon2 CỐ TÌNH chậm,
thường 50–200 ms, để chống dò mật khẩu bằng vét cạn.
-> với endpoint đăng nhập, phần lớn TTFB có thể là băm mật khẩu,
không phải database.Muốn tách chặng 6 và 7, phải đo từ phía máy chủ. Thêm đoạn sau vào Program.cs của API ở bài 0.5, chia làm hai chỗ: hai dòng using đặt ở đầu file, còn app.Use(...) đặt ngay sau dòng var app = builder.Build();. Đây là một middleware: đoạn code chạy với mọi request trước và sau endpoint. Bài 8.2 dạy kỹ.
using System.Diagnostics; // Stopwatch
using System.Globalization; // CultureInfo
// ... phần code sẵn có ...
// var app = builder.Build(); <- đoạn dưới đặt ngay sau dòng này
app.Use(async (ctx, next) =>
{
var sw = Stopwatch.StartNew();
// Header phải gắn TRƯỚC khi response bắt đầu gửi đi.
// OnStarting chạy đúng lúc đó, khi endpoint đã xử lý xong.
ctx.Response.OnStarting(() =>
{
ctx.Response.Headers["Server-Timing"] = string.Create(
CultureInfo.InvariantCulture, $"app;dur={sw.Elapsed.TotalMilliseconds:F1}");
return Task.CompletedTask;
});
await next();
});Gọi curl -i tới một endpoint, kết quả chạy thật trên .NET 10 (rút gọn, bỏ vài header):
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Server: Kestrel
Server-Timing: app;dur=3.0Hai lỗi hay gặp khi tự viết đoạn này:
// SAI: gán header SAU await next()
app.Use(async (ctx, next) =>
{
var sw = Stopwatch.StartNew();
await next();
ctx.Response.Headers["Server-Timing"] = $"app;dur={sw.Elapsed.TotalMilliseconds:F1}";
});System.InvalidOperationException: Headers are read-only, response has already started.- Endpoint đã ghi body thì header đã được gửi đi trước body, không sửa được nữa. Client vẫn nhận
200nhưng không cóServer-Timing, còn log máy chủ ghi ngoại lệ trên. - Không có
CultureInfo.InvariantCulture, máy đặt định dạng tiếng Việt sẽ inapp;dur=0,1với dấu phẩy. Trình duyệt không hiểu giá trị đó.
Header Server-Timing hiện THẲNG trong tab Timing của DevTools
-> nối được số liệu máy chủ với số liệu trình duyệtHai điều nữa quan sát được trong cùng tab Network:
1. Request thứ hai nhanh hơn hẳn.
Request 1: DNS 24 ms + connection 48 ms = 72 ms chi phí thiết lập
Request 2: DNS 0 ms + connection 0 ms = 0 ms
-> DNS đã nằm trong bộ nhớ đệm, kết nối được dùng lại (keep-alive)2. Token đi đâu sau khi nhận được.
Xem request TIẾP THEO sau khi đăng nhập:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Hoặc, nếu dùng cookie:
Set-Cookie: .AspNetCore.Identity.Application=...; HttpOnly; Secure; SameSite=LaxHttpOnly là khác biệt quan trọng: JavaScript KHÔNG đọc được cookie đó
-> một lỗ hổng XSS không lấy được phiên đăng nhập
-> token lưu trong localStorage thì ngược lạiBài 2 — Thử DNS bằng tay
Chạy nslookup cho ba tên miền bất kỳ, rồi thử truy cập bằng IP thay vì tên miền.
Tiêu chí hoàn thành: bạn thấy truy cập bằng IP trần không ra đúng trang, và giải thích được thứ gì mới thật sự chọn ra trang web.
Gợi ý và lời giải — Bài 2
Gợi ý. Một địa chỉ IP thường phục vụ hàng nghìn tên miền. Máy chủ dựa vào đâu để biết bạn muốn trang nào?
Lời giải — tra DNS:
for d in example.com github.com microsoft.com; do
echo "--- $d"; nslookup $d | grep -A3 "Non-authoritative"
doneKết quả chạy thật:
--- example.com
Non-authoritative answer:
Name: example.com
Address: 172.66.147.243
Name: example.com
Address: 104.20.23.154
--- github.com
Non-authoritative answer:
Name: github.com
Address: 20.205.243.166
--- microsoft.com
Non-authoritative answer:
Name: microsoft.com
Address: 150.171.110.68
Name: microsoft.com
Address: 2603:1061:14:16b::1Ba quan sát ngay từ đây:
1. example.com có HAI địa chỉ IPv4 -> cân bằng tải ở tầng DNS
2. microsoft.com có cả IPv4 lẫn IPv6 (bản ghi A và AAAA)
3. "Non-authoritative answer" nghĩa là câu trả lời lấy từ bộ nhớ đệm
của resolver, không phải hỏi thẳng máy chủ tên miền gốcGiờ thử truy cập bằng IP:
curl -s -o /dev/null -w "IP trần: %{http_code}\n" http://172.66.147.243/
curl -s -o /dev/null -w "IP + Host header: %{http_code}\n" -H "Host: example.com" http://172.66.147.243/
curl -sk -o /dev/null -w "HTTPS bằng IP (bỏ qua chứng chỉ): %{http_code}\n" \
--resolve example.com:443:172.66.147.243 https://example.com/
curl -s -o /dev/null -w "HTTPS bằng IP trực tiếp: %{http_code} ssl=%{ssl_verify_result}\n" \
https://172.66.147.243/Kết quả chạy thật:
IP trần: 403
IP + Host header: 200
HTTPS bằng IP (bỏ qua chứng chỉ): 200
HTTPS bằng IP trực tiếp: 000 ssl=1Bốn dòng này trả lời câu hỏi chính:
IP trần -> 403
Máy chủ nhận được request nhưng KHÔNG BIẾT bạn muốn trang nào
-> nó từ chối
IP + Host: example.com -> 200
Header Host là thứ CHỌN RA trang webNói cách khác: DNS chỉ đưa bạn TỚI ĐÚNG MÁY.
Header Host mới nói bạn muốn TRANG NÀO trên máy đó.GET / HTTP/1.1
Host: example.com <- dòng này chọn trang webMột máy chủ có thể phục vụ hàng nghìn tên miền trên cùng một IP.
Đây gọi là virtual hosting, và header Host là cơ chế duy nhất phân biệt chúng.Và dòng thứ tư nói về HTTPS — nó còn chặt hơn:
https://172.66.147.243/ -> ssl_verify_result=1, kết nối THẤT BẠI
Chứng chỉ được cấp cho TÊN MIỀN example.com, không phải cho một địa chỉ IP.
-> trình duyệt so tên trong chứng chỉ với tên bạn gõ -> không khớp -> từ chốiCòn --resolve example.com:443:172.66.147.243 thì thành công, vì:
- curl vẫn gửi tên example.com trong SNI và trong header Host
- nó chỉ bỏ qua bước hỏi DNS, dùng IP bạn chỉ định
-> chứng chỉ khớp -> 200SNI — Server Name Indication — là phiên bản của header Host ở tầng TLS:
Vấn đề: bắt tay TLS xảy ra TRƯỚC khi gửi header HTTP
-> máy chủ phải chọn chứng chỉ nào mà chưa biết bạn muốn trang gì
SNI giải quyết: tên miền được gửi kèm ngay trong bước bắt tay TLS
-> máy chủ chọn đúng chứng chỉ
Và vì nó nằm ngoài phần mã hoá, SNI là thứ duy nhất trong một kết nối HTTPS
mà người quan sát trên đường truyền đọc được — họ biết bạn vào trang nào,
nhưng không biết bạn xem gì trên đó.Ba lệnh nữa đáng biết khi gỡ lỗi DNS:
dig +short crm.company.com # gọn hơn nslookup
dig crm.company.com +trace # xem toàn bộ chuỗi phân giải từ gốc
dig @8.8.8.8 crm.company.com # hỏi một resolver KHÁCLệnh thứ ba hữu ích nhất khi gặp "trang này chạy ở máy tôi mà không chạy ở máy anh":
hai máy dùng hai resolver khác nhau, và một cái còn giữ bản ghi CŨ trong đệm
-> so sánh kết quả từ hai resolver là cách nhanh nhất xác nhận giả thuyết đóBài 3 — Tự vẽ sơ đồ
Không nhìn bài, vẽ lại tám chặng và ghi tên bài học tương ứng với mỗi chặng.
Tiêu chí hoàn thành: bạn vẽ được đủ tám chặng, và với mỗi chặng nêu được một lỗi cụ thể có thể xảy ra ở đó.
Gợi ý và lời giải — Bài 3
Gợi ý. Nhớ được tám chặng là một chuyện. Biết chặng nào gây lỗi gì mới là thứ dùng được khi có sự cố.
Lời giải — sơ đồ kèm lỗi đặc trưng của từng chặng:
1. Gõ URL
Lỗi: gõ sai, hoặc trình duyệt tự thêm https:// vào một địa chỉ chỉ có http
2. DNS: tên miền -> IP [bài 2.2]
Lỗi: ERR_NAME_NOT_RESOLVED
tên miền hết hạn, bản ghi sai, hoặc resolver còn giữ bản ghi cũ
Chẩn đoán: dig +short, dig @8.8.8.8
3. TCP: thiết lập kết nối [bài 2.2]
Lỗi: ERR_CONNECTION_REFUSED -> không có gì lắng nghe ở cổng đó
ERR_CONNECTION_TIMED_OUT -> tường lửa chặn, hoặc máy chủ không phản hồi
Chẩn đoán: telnet <host> 443, hoặc nc -zv <host> 443
4. TLS: mã hoá kênh truyền [bài 2.2, 2.4]
Lỗi: ERR_CERT_DATE_INVALID -> chứng chỉ hết hạn
ERR_CERT_COMMON_NAME_INVALID -> chứng chỉ cấp cho tên khác
Chẩn đoán: openssl s_client -connect host:443 -servername host
5. HTTP: POST /api/auth/login [bài 2.4, 2.5]
Lỗi: 400 body sai định dạng
404 sai đường dẫn
405 sai method
415 thiếu Content-Type
Chẩn đoán: đọc thẳng tab Network, hoặc curl -v
6. Backend nhận và xử lý [bài 2.3]
Lỗi: 500 ngoại lệ chưa xử lý
502 gateway không gọi được ứng dụng
503 ứng dụng chưa sẵn sàng
Chẩn đoán: log của ứng dụng, và traceId trong phản hồi
7. Database: kiểm tra tài khoản [bài 2.6]
Lỗi: timeout, pool cạn, truy vấn chậm
-> thường biểu hiện ra ngoài thành 500 hoặc 504
Chẩn đoán: log truy vấn, thời gian chờ lấy kết nối
8. Trả JWT về trình duyệt [bài 2.7]
Lỗi: 401 ở các request SAU (token sai, hết hạn, lệch đồng hồ)
lỗi CORS nếu web và API khác origin
Chẩn đoán: giải mã token, so exp với giờ hiện tạiVà đây là cách dùng sơ đồ này khi có sự cố — chia đôi thay vì đi tuần tự:
Thay vì kiểm tra chặng 1, rồi 2, rồi 3...
hãy kiểm tra chặng 5 TRƯỚC:
curl -i https://crm.company.com/api/auth/login -d '...'
Có phản hồi HTTP -> chặng 1–5 đều tốt -> vấn đề ở 6, 7 hoặc 8
Không có -> vấn đề ở 1–4Một lệnh loại bỏ được một nửa số khả năng.Đây là cùng ý tưởng với git bisect ở bài 3.9: chia đôi phạm vi tìm kiếm sau mỗi lần thử.
Bảng ánh xạ nhanh từ triệu chứng sang chặng:
| Triệu chứng | Chặng | Kiểm tra đầu tiên |
|---|---|---|
| Trang không mở được, không có gì trong Network | 2–3 | dig, nc -zv |
| Cảnh báo bảo mật của trình duyệt | 4 | openssl s_client |
| Có mã trạng thái 4xx | 5 | Đọc body phản hồi |
| Có mã trạng thái 5xx | 6–7 | Log máy chủ theo traceId |
| Đăng nhập được rồi lại bị đẩy ra | 8 | Giải mã token, xem exp |
| "Blocked by CORS policy" | 5 | Header phản hồi, và thứ tự middleware |
Và một bài tập nhỏ để tự kiểm tra: giải thích cho người không làm kỹ thuật.
"Khi anh gõ địa chỉ và bấm Enter:
máy tính hỏi danh bạ xem trang đó nằm ở máy nào,
gọi điện tới máy đó và thống nhất một cách nói chuyện riêng để không ai nghe lén,
gửi câu hỏi kèm tên đăng nhập và mật khẩu,
máy bên kia tra sổ xem có đúng không,
rồi đưa lại một tấm vé để lần sau khỏi phải đăng nhập nữa."Giải thích được bằng ngôn ngữ đó nghĩa là bạn đã hiểu, không phải đã thuộc.
Và đó cũng là kỹ năng cần khi phải nói chuyện với người dùng trong một sự cố.Tự kiểm tra
Vì sao nên học từ trải nghiệm tới thuật ngữ?
Vì học thuộc định nghĩa thì quên sau vài ngày, còn tự tay chạy nslookup rồi thử gõ IP vào trình duyệt thì thấy ngay DNS làm được gì và không làm được gì, và nhớ lâu hơn. Mỗi khái niệm nên có một cách tự thử.
Tám chặng của một request web là gì?
Gõ URL, phân giải DNS, thiết lập kết nối TCP, bắt tay TLS, gửi request HTTP, backend xử lý, truy vấn database, và trả kết quả kèm token về trình duyệt.
Mã lỗi ERR_CONNECTION_REFUSED cho biết vấn đề ở chặng nào?
Chặng TCP. Tên miền đã phân giải ra IP thành công nhưng không mở được kết nối, thường vì server không chạy hoặc sai cổng.
Lỗi 401 khác lỗi 404 ở chỗ nào về mặt chẩn đoán?
404 là sai đường dẫn ở chặng HTTP, tức route không tồn tại. 401 là vấn đề ở chặng xác thực, tức token thiếu, sai hoặc hết hạn, còn đường dẫn thì đúng.
Nếu chỉ có hai giờ thì nên học bài nào?
Bài về HTTP và bài về xác thực, vì hai mảng đó chiếm phần lớn công việc hàng ngày của một backend engineer.
Công cụ quan trọng nhất để quan sát mọi thứ trong module này là gì?
DevTools của trình duyệt, tab Network. Ở đó bạn thấy được request, header, mã trạng thái và thời gian của từng chặng.
Kết luận
Ba điều đáng nhớ nhất:
- Một ví dụ xuyên suốt nối mọi khái niệm lại — dễ nhớ hơn học từng thuật ngữ rời.
- Mã lỗi cho biết chặng nào có vấn đề. Đây là kỹ năng chẩn đoán cơ bản nhất.
- Tự tay thử mỗi khái niệm. Một lệnh
nslookupđáng giá hơn một đoạn định nghĩa.
Tham khảo
Điều hướng
- Bài trước: Không có (bài mở đầu module).
- Bài tiếp theo: 2.2 — Internet hoạt động như thế nào
- Về module: Trang mục lục