Content-Type là một tiêu đề HTTP chỉ định định dạng dữ liệu được truyền giữa máy khách và máy chủ. Nếu không có loại MIME chính xác, trình duyệt không thể xử lý phản hồi đúng cách: tệp văn bản hiển thị dưới dạng mã thô và hình ảnh không mở được. Theo MDN Web Docs, 2025, Content-Type là bắt buộc để truyền dữ liệu đúng cách ở bất kỳ loại nào trong giao thức HTTP và xác định cách người nhận diễn giải phần thân của thông điệp.
Những điểm chính
Content-Type là một tiêu đề HTTP thuộc nhóm tiêu đề biểu diễn, cho người nhận biết định dạng dữ liệu trong phần thân của thông điệp. Nó là bắt buộc đối với các yêu cầu và phản hồi HTTP có chứa phần thân và nếu không có nó, máy khách không thể diễn giải chính xác các byte nhận được. Dựa trên Content-Type, trình duyệt hoặc ứng dụng di động chọn trình phân tích: đối với text/html, nó khởi chạy công cụ HTML; đối với image/png — bộ giải mã PNG; đối với application/json — trình phân tích JSON.
Giá trị của Content-Type là một loại MIME — một định danh chuẩn hóa cho các định dạng dữ liệu. Từ viết tắt MIME là viết tắt của Multipurpose Internet Mail Extensions, vì tiêu chuẩn này ban đầu được tạo ra cho các tệp đính kèm email. Tuy nhiên, nó đã trở thành nền tảng của HTTP và ngày nay được sử dụng ở khắp mọi nơi — từ truyền tải trang web đến trao đổi dữ liệu trong API REST. Mỗi loại MIME bao gồm hai phần: một danh mục chính và một loại phụ được phân cách bằng dấu gạch chéo.
Tham số charset bổ sung cho Content-Type đối với các định dạng văn bản. Ví dụ: Content-Type: text/html; charset=utf-8 cho biết một tài liệu HTML đang được truyền với mã hóa UTF-8. Theo IETF RFC 7231, phần 3.1.1.5, tiêu đề Content-Type là bắt buộc đối với các thông điệp HTTP có chứa phần thân và việc thiếu nó được coi là application/octet-stream hoặc dẫn đến MIME sniffing.
Giao thức HTTP/0.9, được phát hành vào năm 1991, chỉ truyền các trang HTML, do đó loại dữ liệu được ngầm hiểu theo mặc định. Với sự ra đời của HTTP/1.0 trong RFC 1945, các nhà phát triển nhận ra nhu cầu truyền hình ảnh, biểu định kiểu và tập lệnh. Họ đã điều chỉnh tiêu chuẩn MIME từ giao thức email và Content-Type trở thành một phần không thể thiếu của HTTP. Kể từ đó, sổ đăng ký IANA đã mở rộng đến hàng trăm giá trị — từ text/html quen thuộc đến image/avif và application/manifest+json hiện đại.
Content-Type đóng một vai trò quan trọng trong việc bảo vệ khỏi các cuộc tấn công. Nếu máy chủ gửi tệp HTML với loại MIME text/plain, trình duyệt sẽ không thực thi JavaScript hoặc xây dựng DOM — điều này ngăn chặn các cuộc tấn công XSS. Tiêu đề X-Content-Type-Options: nosniff, được OWASP khuyến nghị, hoàn toàn cấm trình duyệt đoán loại MIME dựa trên nội dung. Theo PortSwigger Research, các cuộc tấn công MIME sniffing đặc biệt phổ biến trong Internet Explorer 6-9, nơi trình duyệt bỏ qua Content-Type và xác định loại từ các byte đầu tiên của tệp.
Loại MIME được chỉ định theo định dạng type/subtype, trong đó type là danh mục dữ liệu chung và subtype là định dạng cụ thể trong đó. Ví dụ: trong image/png, danh mục image chỉ định một hình ảnh và loại phụ png chỉ định định dạng Portable Network Graphics. Chỉ có một số danh mục: text, image, audio, video, application, multipart và message. Phần còn lại của sự đa dạng đến từ các loại phụ, với số lượng lên đến hàng trăm.
Các tham số bổ sung được truyền qua dấu chấm phẩy sau loại phụ. Tham số phổ biến nhất là charset để chỉ định mã hóa. Content-Type: application/json; charset=utf-8 cho biết một tài liệu JSON đang được truyền với mã hóa UTF-8. Về mặt hình thức, charset cho application/json là dư thừa vì JSON luôn ở dạng UTF-8 theo RFC 8259, nhưng việc chỉ định rõ ràng cải thiện khả năng tương thích với các máy khách HTTP cũ.
| Danh mục | Ví dụ loại phụ | Mô tả |
|---|---|---|
| text | html, plain, css, javascript, csv | Định dạng văn bản có thể đọc được |
| image | jpeg, png, gif, webp, svg+xml, avif | Hình ảnh raster và vector |
| audio | mpeg, ogg, wav, mp4, webm | Định dạng âm thanh để phát trực tuyến |
| video | mp4, webm, ogg, x-msvideo, 3gpp | Định dạng video và bộ chứa đa phương tiện |
| application | json, xml, pdf, zip, octet-stream, protobuf | Dữ liệu nhị phân và có cấu trúc |
| multipart | form-data, mixed, alternative, byteranges | Tài liệu tổng hợp gồm nhiều phần |
Các loại MIME tiêu chuẩn được đăng ký trong sổ đăng ký IANA và có tiền tố danh mục chính. Các loại không tiêu chuẩn (dành riêng cho nhà cung cấp) sử dụng tiền tố x- hoặc định dạng vnd.company.type — ví dụ: application/vnd.google-earth.kml+xml cho định dạng KML của Google. Trình duyệt có thể không nhận dạng được các loại không tiêu chuẩn, do đó đối với các tệp đính kèm không xác định, người ta sử dụng application/octet-stream — một luồng nhị phân phổ quát mà trình duyệt không cố gắng hiển thị mà đề xuất tải xuống dưới dạng tệp.
Tham số charset rất quan trọng để hiển thị văn bản chính xác. Nếu không có nó, trình duyệt có thể diễn giải sai ký tự, dẫn đến mojibake. Tiêu chuẩn cho web là UTF-8, nhưng cũng có thể gặp ISO-8859-1 (Latin-1) cho các ngôn ngữ Tây Âu và windows-1251 cho chữ Kirin trên các trang web cũ. Khuyến nghị của W3C là luôn chỉ định charset=utf-8 cho text/html và text/plain, trong khi charset không cần thiết cho application/json.
Trong thực tế, các nhà phát triển web và di động làm việc với một tập hợp giới hạn các loại MIME. Biết các loại này là điều cần thiết để cấu hình máy chủ chính xác, viết máy khách HTTP và xử lý các tệp tĩnh. text/html là loại chính cho các trang web, được máy chủ Apache và Nginx trả về theo mặc định cho các tệp HTML. application/xhtml+xml ít được sử dụng hơn và chỉ dành cho các tài liệu XHTML.
application/json đã trở thành tiêu chuẩn cho các API REST. Máy chủ trả về dữ liệu JSON với loại MIME này và máy khách gửi nó trong các yêu cầu POST và PUT. text/javascript (không được khuyến khích) và application/javascript được sử dụng cho các tệp JavaScript. Theo Khảo sát W3Techs, 2025, JSON là định dạng dữ liệu phát triển nhanh nhất trên web, vượt qua XML vào năm 2018. Đối với các dịch vụ SOAP, text/xml hoặc application/soap+xml vẫn được sử dụng.
Đối với hình ảnh, loại MIME được xác định bởi định dạng tệp: image/jpeg cho JPEG, image/png cho PNG, image/gif cho GIF, image/webp cho định dạng WebP hiện đại. image/svg+xml được sử dụng cho đồ họa vector và hỗ trợ các kiểu và tập lệnh nhúng. video/mp4, audio/mpeg và application/pdf là các loại khác thường gặp. Đối với phông chữ web, font/woff2, font/woff và font/ttf được sử dụng.
Khi tải tệp lên qua biểu mẫu HTML, multipart/form-data được sử dụng — một loại MIME tổng hợp chia yêu cầu thành nhiều phần. Mỗi phần có tiêu đề Content-Type riêng và Content-Disposition chỉ định tên trường và tên tệp gốc. Máy chủ nhận tệp với loại MIME thực tế do trình duyệt xác định và có thể xác minh nó ở phía phụ trợ. application/octet-stream được sử dụng cho các tệp có loại không xác định — trình duyệt không cố gắng hiển thị nội dung mà đề xuất lưu vào đĩa.
Loại MIME ảnh hưởng đến chính sách bộ nhớ đệm của CDN và trình duyệt. Hình ảnh có URL ổn định thường được lưu trong bộ nhớ đệm trong thời gian dài (một năm hoặc hơn), trong khi các trang HTML được lưu trong bộ nhớ đệm trong vài phút hoặc vài giây. Máy chủ CDN như Cloudflare và Akamai sử dụng Content-Type để chọn thuật toán nén: text/* được nén bằng gzip hoặc brotli, image/* thì không, vì hình ảnh đã được nén. Cấu hình Content-Type chính xác trên máy chủ ảnh hưởng trực tiếp đến hiệu suất tải của các trang web và ứng dụng di động.
Máy chủ đặt tiêu đề Content-Type trong phản hồi HTTP dựa trên loại tệp được yêu cầu hoặc nội dung được tạo động. Các máy chủ web phổ biến như Nginx và Apache có bảng loại MIME tích hợp ánh xạ phần mở rộng tệp với Content-Type tương ứng. Ví dụ: index.html nhận text/html và style.css nhận text/css. Đối với các phản hồi động, nhà phát triển đặt Content-Type trong mã ứng dụng bằng PHP, Python, Java hoặc Kotlin.
Máy khách sử dụng Content-Type để chọn bộ xử lý. Nếu máy chủ trả về text/html, trình duyệt khởi chạy trình phân tích HTML và xây dựng cây DOM. Nếu image/png — khởi chạy bộ giải mã PNG. Nếu Content-Type bị thiếu hoặc không chính xác, máy khách áp dụng MIME sniffing — cố gắng đoán loại dựa trên chữ ký (byte ma thuật) ở đầu tệp. JPEG bắt đầu bằng các byte FF D8 FF, PNG bắt đầu bằng 89 50 4E 47 và PDF bắt đầu bằng 25 50 44 46. Quá trình này có khả năng nguy hiểm và bị vô hiệu hóa bởi tiêu đề X-Content-Type-Options: nosniff.
Trong các ứng dụng di động, Content-Type được xử lý bởi các máy khách HTTP. OkHttp trên Android tự động phân tích tiêu đề Content-Type từ phản hồi và cung cấp thông qua phương thức Response.header("Content-Type"). Máy khách URLSession trên iOS thực hiện tương tự thông qua thuộc tính URLResponse.mimeType. Trên cả hai nền tảng, Content-Type được sử dụng để chọn trình phân tích: JSON — qua Moshi hoặc Gson trên Android, qua Codable trên iOS; hình ảnh — qua Glide, Coil hoặc SDWebImage.
Thương lượng nội dung là một cơ chế HTTP trong đó máy khách chỉ định định dạng phản hồi mong muốn thông qua tiêu đề Accept và máy chủ chọn định dạng phù hợp và trả về với Content-Type tương ứng. Ví dụ: máy khách gửi Accept: application/json, máy chủ phản hồi với Content-Type: application/json. Nếu máy chủ không thể cung cấp định dạng được yêu cầu, nó trả về 406 Not Acceptable. Trong API REST, cơ chế này cho phép một điểm cuối duy nhất trả về dữ liệu ở định dạng JSON, XML hoặc HTML.
Tiêu đề Content-Type được sử dụng trong cả yêu cầu HTTP và phản hồi HTTP. Trong yêu cầu, nó chỉ định định dạng của phần thân yêu cầu, ví dụ khi gửi JSON qua POST. Trong phản hồi, nó chỉ định định dạng của dữ liệu được trả về. Sự khác biệt chính là Content-Type của yêu cầu do máy khách đặt, trong khi Content-Type của phản hồi do máy chủ đặt. Việc đặt sai Content-Type trong yêu cầu khiến máy chủ không thể phân tích phần thân, trả về lỗi 400 Bad Request hoặc 415 Unsupported Media Type.
Trong yêu cầu HTTP, Content-Type là bắt buộc đối với các phương thức POST, PUT và PATCH nếu yêu cầu có chứa phần thân. GET, HEAD và DELETE thường không sử dụng phần thân, do đó Content-Type không được chỉ định hoặc bị bỏ qua đối với chúng. Khi gửi biểu mẫu HTML có thuộc tính enctype="multipart/form-data", trình duyệt tự động đặt Content-Type: multipart/form-data với một chuỗi ranh giới duy nhất để phân cách các phần của yêu cầu tổng hợp. Mỗi phần được phân cách bằng --boundary và kết thúc yêu cầu được đánh dấu bằng --boundary--.
Trong phản hồi HTTP, Content-Type do máy chủ đặt. Nếu máy chủ không chỉ định Content-Type, máy khách sẽ bật MIME sniffing hoặc xử lý phản hồi dưới dạng application/octet-stream. Phương thức HTTP HEAD cho phép lấy các tiêu đề phản hồi, bao gồm Content-Type, mà không truyền phần thân. Điều này hữu ích để kiểm tra loại tài nguyên trước khi tải đầy đủ. Máy chủ CDN có thể ghi đè Content-Type khi biến đổi nội dung — ví dụ: khi chuyển đổi hình ảnh sang WebP.
import okhttp3.*
fun checkContentType() {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://api.example.com/resource")
.head()
.build()
client.newCall(request).execute().use { response ->
val contentType = response.header("Content-Type")
val mediaType = MediaType.parse(contentType)
println("Loại: ${mediaType?.type}, Loại phụ: ${mediaType?.subtype}")
}
}
Trong phát triển di động, tiêu đề Content-Type được xử lý tự động bởi các máy khách HTTP. Trong OkHttp trên Android, Content-Type được đặt qua RequestBody: val body = "{}".toRequestBody("application/json".toMediaType()). Retrofit quản lý Content-Type thông qua các chú thích: @Body cho JSON, @Part cho multipart. Trên iOS, URLSession đặt Content-Type cho HTTPBody và Alamofire thực hiện việc này thông qua tham số encoding: JSONEncoding.default hoặc URLEncoding.default. Việc đặt Content-Type thủ công là cần thiết khi làm việc với socket thô hoặc giao thức tùy chỉnh.
Content-Type không chính xác là một trong những vấn đề phổ biến nhất trong phát triển và tích hợp dịch vụ web. Lỗi thường gặp nhất là khi máy chủ trả về text/html thay vì application/json. Máy khách nhận JSON dưới dạng chuỗi HTML, không thể phân tích và đưa ra ngoại lệ. Điều này xảy ra khi framework web được cấu hình cho HTML theo mặc định và nhà phát triển quên ghi đè Content-Type cho các điểm cuối API. Trong PHP, điều này thể hiện khi thiếu header('Content-Type: application/json'), trong Spring Boot — khi thiếu chú thích produces.
Lỗi phổ biến thứ hai là charset không chính xác hoặc bị thiếu. Nếu máy chủ gửi text/html; charset=iso-8859-1 và trình duyệt mong đợi UTF-8, các ký tự Kirin hiển thị dưới dạng mojibake. Vấn đề này điển hình cho các trang web cũ chưa chuyển sang UTF-8. Đối với JSON, lỗi này ít phổ biến hơn vì RFC 8259 quy định UTF-8 mà không cần thương lượng thêm. Giải pháp là luôn chỉ định rõ ràng charset=utf-8 cho các loại MIME văn bản trong cấu hình máy chủ.
Vấn đề thứ ba là sự không khớp giữa Content-Type và nội dung thực tế. Nếu máy chủ gửi Content-Type: image/png nhưng phần thân phản hồi chứa hình ảnh WebP, trình duyệt có thể không giải mã được. Máy chủ CDN đôi khi nén hình ảnh với việc thay đổi định dạng nhưng không cập nhật tiêu đề Content-Type. Kiểm tra sự phù hợp của Content-Type với nội dung thực tế là một bước bắt buộc trong kiểm thử API và kiểm thử tích hợp các ứng dụng di động.
Để gỡ lỗi, hãy sử dụng công cụ phát triển của trình duyệt (tab Network), curl với cờ -I để kiểm tra tiêu đề phản hồi hoặc các trình bắt gói tin như Charles Proxy và Wireshark. Nginx được cấu hình qua chỉ thị include mime.types, Apache — qua AddType và AddDefaultCharset. Đối với các tệp tĩnh, luôn kiểm tra xem phần mở rộng tệp có khớp với loại MIME của nó không. Đối với các phản hồi động trong tất cả các ngôn ngữ lập trình, hãy đặt rõ ràng Content-Type trước khi xuất dữ liệu — điều này ngăn ngừa phần lớn các vấn đề.
Câu hỏi thường gặp
Nếu không có Content-Type, trình duyệt bật MIME sniffing — phân tích các byte đầu tiên của phản hồi để tự động xác định loại dữ liệu. Điều này có thể dẫn đến xử lý nội dung không chính xác và lỗ hổng bảo mật. Các trình duyệt hiện đại có tiêu đề X-Content-Type-Options: nosniff chặn hoàn toàn việc đoán.
Content-Type chỉ định định dạng của dữ liệu được truyền trong thông điệp hiện tại (phần thân yêu cầu hoặc phản hồi). Accept là tiêu đề yêu cầu cho máy chủ biết định dạng phản hồi mà máy khách ưu tiên. Content-Type do người gửi dữ liệu đặt, trong khi Accept do người nhận đặt và cả hai tham gia vào cơ chế thương lượng nội dung.
Loại MIME chính thức cho JSON là application/json theo RFC 8259. Trước đây, text/x-json được sử dụng nhưng loại này đã lỗi thời. Tham số charset cho application/json là không cần thiết vì JSON luôn được truyền với mã hóa UTF-8, UTF-16 hoặc UTF-32 có tự động phát hiện thứ tự byte (BOM) theo đặc tả.
Điều này xảy ra khi framework web không ghi đè Content-Type mặc định cho các điểm cuối API. Trong PHP, nó được sửa bằng cách gọi header('Content-Type: application/json'), trong Spring Boot — bằng chú thích @GetMapping(produces = "application/json"), trong Express.js — bằng phương thức res.set('Content-Type', 'application/json').
application/octet-stream là loại MIME phổ quát cho dữ liệu nhị phân có định dạng không xác định. Trình duyệt không cố gắng hiển thị tệp như vậy trong cửa sổ mà đề xuất lưu vào đĩa. Nó được sử dụng cho tải tệp xuống, tệp đính kèm email và dữ liệu truyền phát khi máy chủ không thể xác định loại chính xác của nội dung được truyền.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm