Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn gửi một bưu kiện. Bên trong là món hàng (đó là body của request), nhưng bên ngoài thùng còn dán một loạt nhãn: địa chỉ người nhận, loại hàng (dễ vỡ hay không), ai được phép mở, có cần giao hỏa tốc không. Bưu tá đọc những nhãn đó trước khi chạm vào món hàng để biết phải xử lý ra sao. Trong thế giới HTTP, những "nhãn dán" đó chính là HTTP Headers.
Ở những bài trước bạn đã nắm cấu trúc đầy đủ của một HTTP request/response (Bài 5) và sẽ còn đi sâu vào status code (Bài 9). Nhưng headers là phần mà rất nhiều BA bỏ qua, để rồi khi ngồi họp với team kỹ thuật thì lúng túng. Khi dev nói "API trả về 415 vì sai Content-Type", hoặc "client gọi mà thiếu Authorization header nên bị 401", hoặc "cái màn hình này load chậm vì Cache-Control không đúng" — nếu bạn không hiểu headers, bạn sẽ chỉ gật gù mà không thực sự nắm được vấn đề, không thể viết được spec chuẩn, càng không thể nghiệm thu (UAT) một cách tự tin.
Headers là metadata — dữ liệu mô tả về dữ liệu. Chúng quyết định một request có được chấp nhận hay không, dữ liệu được hiểu theo định dạng nào, ai được phép truy cập, và kết quả có được lưu cache để lần sau nhanh hơn không. Trong bài này, chúng ta sẽ tập trung sâu vào ba nhóm header quan trọng nhất mà một BA cần thuộc nằm lòng: Content-Type, Authorization, và Cache-Control. Đây không phải kiến thức "để biết", mà là công cụ bạn dùng hằng ngày khi đọc tài liệu API, viết acceptance criteria, và debug cùng team.
Khái niệm cốt lõi
Header là gì và nằm ở đâu
Mỗi HTTP message (cả request lẫn response) gồm ba phần: dòng đầu (start line), phần headers, và phần body. Headers nằm giữa, được viết theo cặp Tên: Giá-trị, mỗi cặp một dòng:
POST /api/v1/orders HTTP/1.1
Host: api.tiki.vn
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Cache-Control: no-cache{ "product_id": 12345, "quantity": 2 }
Phần phía trên dòng trống là headers; phần dưới dòng trống là body. Lưu ý: tên header không phân biệt hoa thường (Content-Type và content-type là một), nhưng theo quy ước người ta viết kiểu Title-Case cho dễ đọc.
Có hàng trăm header trong chuẩn HTTP, nhưng đừng hoảng. Với vai trò BA, bạn chỉ cần nắm chắc một nhóm nhỏ nhưng xuất hiện ở 90% các tình huống thực tế.
Content-Type — "Dữ liệu này thuộc định dạng gì?"
Content-Type cho bên nhận biết body được mã hóa theo định dạng nào, để bên nhận biết cách "đọc" nó. Đây là header thường gây lỗi nhất khi tích hợp.
Một vài giá trị bạn sẽ gặp liên tục:
application/json— body là JSON. Phổ biến nhất trong API hiện đại.application/x-www-form-urlencoded— dữ liệu form kiểukey=value&key2=value2, hay gặp khi submit form HTML truyền thống.multipart/form-data— dùng khi upload file (kèm cả dữ liệu text). Mỗi phần được phân tách bằng "boundary".application/xmlhoặctext/xml— XML, thường gặp ở hệ thống cũ (legacy), ngân hàng, bảo hiểm.text/html,text/plain— HTML hoặc văn bản thuần.
Content-Type trong request mô tả body mà client gửi lên. Còn Content-Type trong response mô tả body mà server trả về. Hai cái này có thể khác nhau hoàn toàn. Ví dụ client gửi application/json, nhưng server trả về application/pdf (một file hóa đơn).Có một người anh em sinh đôi là header Accept. Nếu Content-Type nói "đây là định dạng tôi đang gửi", thì Accept nói "đây là định dạng tôi muốn nhận lại". Client gửi Accept: application/json nghĩa là "làm ơn trả lời tôi bằng JSON". Server sẽ dựa vào đó để chọn định dạng phản hồi (cơ chế này gọi là content negotiation).
Authorization — "Anh là ai, và anh có quyền gì?"
Authorization là header mang thông tin xác thực (credentials) để server biết request đến từ ai và có được phép thực hiện hành động không. Nếu thiếu hoặc sai, bạn thường nhận về status 401 Unauthorized (chưa xác thực) hoặc 403 Forbidden (đã xác thực nhưng không đủ quyền).
Các "kiểu" (scheme) Authorization phổ biến — bài 16, 17, 18 sẽ đi sâu, ở đây chỉ cần nhận diện được:
- Basic:
Authorization: Basic dXNlcjpwYXNz— username:password được mã hóa Base64. Đơn giản nhưng yếu, gần như bắt buộc phải đi kèm HTTPS. - Bearer:
Authorization: Bearer <token>— mang theo một token (thường là JWT hoặc OAuth token). Phổ biến nhất trong API hiện đại. - API Key: đôi khi đặt trong
Authorization, nhưng cũng rất hay đặt ở một header riêng nhưX-API-Key: abc123.
Authorization chứa thông tin nhạy cảm. Khi bạn chụp màn hình log để gửi cho team, hoặc đưa ví dụ vào tài liệu, luôn che/ẩn (mask) giá trị này. Một token bị lộ có thể cho phép kẻ xấu mạo danh người dùng.Cache-Control — "Dữ liệu này lưu lại được không, và bao lâu?"
Cache-Control điều khiển việc caching — tức là lưu tạm dữ liệu để lần sau lấy lại nhanh hơn, không phải hỏi server lại từ đầu. Nó ảnh hưởng trực tiếp đến hiệu năng và cả tính đúng đắn của dữ liệu mà người dùng nhìn thấy.
Vài chỉ thị (directive) hay gặp:
no-store— tuyệt đối không lưu. Dùng cho dữ liệu nhạy cảm (thông tin tài khoản ngân hàng, số dư).no-cache— được lưu, nhưng trước khi dùng lại phải hỏi server xem có còn mới không (revalidate).max-age=3600— dữ liệu được coi là "còn tươi" trong 3600 giây (1 giờ); trong thời gian đó client cứ dùng bản đã lưu, không cần hỏi lại.public— bất kỳ cache nào (kể cả CDN, proxy trung gian) đều được lưu.private— chỉ trình duyệt của riêng người dùng được lưu, các cache chung không được.
Cache-Control thường đi kèm các header bạn cũng nên biết tên: ETag (một "dấu vân tay" của nội dung, để kiểm tra nội dung có thay đổi không) và Expires (cách quy định hết hạn kiểu cũ, đang dần được Cache-Control: max-age thay thế).Đây chính là điểm giao thoa với bài CDN (Bài 36) và caching strategies (Bài 35), nhưng ở góc độ header, bạn chỉ cần hiểu: caching sai có thể khiến người dùng nhìn thấy dữ liệu cũ, hoặc làm hệ thống gọi server quá nhiều lần không cần thiết — cả hai đều là rủi ro nghiệp vụ.
Tình huống thực tế
Tình huống 1: Lỗi 415 khi tích hợp cổng thanh toán tại một sàn TMĐT
Một sàn thương mại điện tử ở TP.HCM tích hợp cổng thanh toán nội bộ. Team frontend báo cáo: mỗi lần bấm "Thanh toán", API trả về 415 Unsupported Media Type và đơn hàng không được tạo. Cả đội mất gần một buổi sáng để dò.
BA của dự án mở Postman, gửi thử request và để ý: frontend đang gửi body dạng JSON, nhưng lại quên đặt header Content-Type: application/json. Mặc định thư viện gửi đi với Content-Type: text/plain. Server backend nhìn thấy "text thuần", từ chối xử lý vì nó chỉ chấp nhận JSON, và trả về 415.
Diễn giải: Status 415 luôn liên quan đến Content-Type. Server "không hiểu" định dạng body, chứ không phải dữ liệu sai. Khi BA nhận diện được mẫu này, đội sửa trong 5 phút.
Bài học rút ra: Khi viết acceptance criteria cho một API có body, BA nên ghi rõ: "Request phải có header Content-Type: application/json". Đây là một dòng nhỏ nhưng tránh được cả buổi debug.
Tình huống 2: Khách hàng thấy giá khuyến mãi đã hết hạn ở một chuỗi bán lẻ
Một chuỗi siêu thị mini có ứng dụng đặt hàng online. Trong đợt khuyến mãi, giá một số sản phẩm được giảm còn 39.000đ. Khi đợt khuyến mãi kết thúc lúc nửa đêm, giá quay về 59.000đ. Nhưng sáng hôm sau, bộ phận chăm sóc khách hàng nhận hàng loạt khiếu nại: app vẫn hiển thị 39.000đ, khách đặt rồi nhưng hệ thống tính 59.000đ ở bước thanh toán.
Nguyên nhân: API trả về danh sách sản phẩm có header Cache-Control: max-age=86400 (24 giờ). Trình duyệt và CDN đã lưu bản giá khuyến mãi và "tin" rằng nó còn tươi suốt một ngày, nên không hỏi lại server dù giá đã đổi.
Diễn giải: Đây là một quyết định về header gây hậu quả nghiệp vụ thật. Dữ liệu giá thay đổi thường xuyên thì không nên cache lâu như vậy.
Bài học rút ra: BA cần đặt câu hỏi "dữ liệu này thay đổi nhanh hay chậm?" khi review spec API. Dữ liệu giá, tồn kho, khuyến mãi nên dùng Cache-Control: no-cache hoặc max-age rất ngắn. Đây chính xác là loại rủi ro mà BA phải nêu ra trong buổi review thiết kế, trước khi nó thành sự cố thật.
Tình huống 3: Tích hợp API ngân hàng và yêu cầu bảo mật header
Một fintech tại Singapore tích hợp với API của một ngân hàng để truy vấn số dư. Ngân hàng yêu cầu mỗi request phải có Authorization: Bearer <token> còn hiệu lực (token hết hạn sau 15 phút), đồng thời response chứa số dư phải có Cache-Control: no-store.
Trong giai đoạn test, BA phát hiện một vấn đề: đội dev tiện tay copy ví dụ request có sẵn token thật vào tài liệu Confluence nội bộ. Token đó tuy ngắn hạn nhưng vẫn là một lỗ hổng — bất kỳ ai đọc trang wiki trong 15 phút đó đều có thể truy vấn số dư khách hàng.
Diễn giải: Header Authorization và Cache-Control: no-store ở đây không chỉ là kỹ thuật, mà là yêu cầu tuân thủ (compliance). Số dư là dữ liệu nhạy cảm, không được lưu ở bất kỳ cache nào.
Bài học rút ra: BA nên đưa "mask token trong mọi tài liệu/log" thành một mục trong Definition of Done, và xác nhận với team rằng các response chứa dữ liệu nhạy cảm có gắn no-store.
Hướng dẫn từng bước
Đây là quy trình BA có thể áp dụng khi làm việc với headers trong một dự án tích hợp API:
- Đọc tài liệu API, lọc ra các header bắt buộc. Mở phần "Request Headers" trong tài liệu (OpenAPI/Swagger — sẽ học ở Bài 41). Liệt kê những header được đánh dấu required: thường là
Content-Type,Authorization, và đôi khiAccept.
- Xác định Content-Type cho cả request và response. Hỏi rõ: client gửi định dạng gì? Server trả về định dạng gì? Ghi cả hai vào spec. Nếu có upload file, đánh dấu
multipart/form-data.
- Làm rõ cơ chế Authorization. Token lấy ở đâu? Hết hạn sau bao lâu? Khi hết hạn thì hệ thống làm gì (báo lỗi hay tự refresh)? Đây là những câu hỏi nghiệp vụ ảnh hưởng đến trải nghiệm người dùng.
- Phân loại dữ liệu theo độ "tươi" để quyết định Cache-Control. Với mỗi endpoint, hỏi "dữ liệu này thay đổi nhanh hay chậm, có nhạy cảm không?". Dữ liệu tĩnh (danh mục tỉnh thành) cache lâu được; dữ liệu động (giá, tồn kho) cache ngắn; dữ liệu nhạy cảm (số dư) thì
no-store.
- Kiểm chứng bằng Postman hoặc curl. Gửi thử request thật (Bài 14, 15), xem tab "Headers" của cả request lẫn response. So sánh thực tế với spec. Đây là bước nghiệm thu cụ thể, không nói suông.
- Viết acceptance criteria có nhắc đến header. Ví dụ: "Given request có
Content-Type: application/jsonhợp lệ vàAuthorizationcòn hiệu lực, When gọi POST /orders, Then trả về 201 và response cóCache-Control: no-store."
Lỗi thường gặp & mẹo
- Nhầm Content-Type của request với của response. Hãy luôn tự hỏi "header này đang nói về body đi lên hay body đi xuống?". Hai cái độc lập nhau.
- Quên Content-Type khi body là JSON. Đây là nguyên nhân số một của lỗi 415. Postman thường tự thêm giúp khi bạn chọn "raw → JSON", nên đôi khi test thấy chạy ngon mà code thật lại lỗi — vì code thật quên đặt header.
- Đặt Cache-Control quá dài cho dữ liệu động. Tiết kiệm tải server nhưng đánh đổi bằng việc người dùng thấy dữ liệu cũ. Với BA, đây là quyết định cân bằng nghiệp vụ, không phải thuần kỹ thuật.
- Để lộ giá trị Authorization. Trong ảnh chụp màn hình, file log, ticket Jira, tài liệu — luôn thay token bằng
*hoặcBearer <token>.
- Tin rằng
no-cachenghĩa là "không cache". Thực rano-cachevẫn cho lưu, chỉ là phải hỏi lại server trước khi dùng. Muốn cấm tuyệt đối thì làno-store. Đây là cặp dễ nhầm nhất.
- Mẹo cho BA: Khi gặp một status code lạ, hãy nghĩ ngay đến header tương ứng.
401/403→ ngóAuthorization.415→ ngóContent-Type.406→ ngóAccept. Phản xạ này giúp bạn dẫn dắt buổi debug thay vì ngồi chờ dev.
Bài tập thực hành
- Đọc và phân loại: Lấy một response thật từ một API công khai (ví dụ một API thời tiết miễn phí). Mở Postman, gửi request và liệt kê toàn bộ header trong response. Chỉ ra:
Content-Typelà gì? CóCache-Controlkhông, giá trị bao nhiêu giây?
- Tự gây lỗi 415: Trong Postman, gửi một POST request với body JSON nhưng cố tình đổi
Content-Typethànhtext/plaintới một API yêu cầu JSON. Quan sát status trả về và ghi lại thông điệp lỗi. Sau đó sửa header lại đúng và xác nhận request thành công.
- Quyết định Cache-Control: Cho 4 endpoint sau, hãy đề xuất giá trị
Cache-Controlhợp lý và giải thích ngắn gọn: (a) danh sách 63 tỉnh thành, (b) giá vàng cập nhật mỗi 5 phút, (c) số dư tài khoản ngân hàng, (d) ảnh logo công ty.
- Viết acceptance criteria: Cho API "POST /api/v1/payments" của một ví điện tử, hãy viết 2 acceptance criteria theo cấu trúc Given-When-Then, trong đó có nhắc tới
Content-Type,Authorization, vàCache-Control.
Tóm tắt
HTTP Headers là phần metadata đi kèm mỗi request và response — những "nhãn dán" giúp hai hệ thống hiểu nhau trước khi chạm vào dữ liệu thật. Ba header mà BA cần thuộc nằm lòng:
- Content-Type: định dạng của body (
application/json,multipart/form-data,application/xml...). Sai header này thường gây lỗi 415. Lưu ý phân biệt header trong request và trong response, và đừng quên người anh emAccept. - Authorization: thông tin xác thực (
Basic,Bearer, API Key). Thiếu/sai dẫn đến 401/403. Là dữ liệu nhạy cảm, luôn phải che khi chia sẻ. - Cache-Control: điều khiển việc lưu cache (
no-store,no-cache,max-age,public/private). Quyết định trực tiếp đến hiệu năng và độ "tươi" của dữ liệu người dùng nhìn thấy.