Last-Modified — bản chất, cơ chế và cấu hình header ngày sửa đổi

Tác giả: IT Sectr Đã đăng: 2026-03-09 Thời gian đọc: 9 phút

Last-Modified là header phản hồi HTTP cho biết ngày và giờ sửa đổi lần cuối cùng của tài nguyên trên máy chủ, cho phép client thực hiện các yêu cầu có điều kiện thông qua If-Modified-Since. Nếu tài nguyên không thay đổi kể từ ngày chỉ định, máy chủ trả về 304 Not Modified mà không gửi phần thân phản hồi, giúp tiết kiệm đáng kể băng thông. Theo RFC 7232 (IETF, 2014), các yêu cầu có điều kiện với Last-Modified giảm thời gian tải trang 30-60% khi truy cập lần sau. Header này được hầu hết các máy chủ HTTP và proxy hỗ trợ tự động.

Điểm chính

  • Last-Modified — HTTP header chứa ngày sửa đổi lần cuối của tài nguyên cho yêu cầu có điều kiện If-Modified-Since
  • 304 Not Modified — phản hồi của máy chủ nếu tài nguyên không thay đổi; client sử dụng bản sao đã lưu trong bộ nhớ đệm
  • Độ chính xác đến giây — giới hạn của header: các thay đổi trong vòng một giây có thể không bị phát hiện
  • Hoạt động cùng với ETag — máy chủ trả về cả hai header, client gửi cả hai yêu cầu có điều kiện
  • Tự động tạo — Nginx và Apache đặt Last-Modified cho tệp tĩnh từ hệ thống tệp

Last-Modified là gì?

Last-Modified là một HTTP header thuộc nhóm header yêu cầu có điều kiện. Máy chủ thêm nó vào phản hồi GET hoặc HEAD, cho biết ngày và giờ sửa đổi lần cuối của tài nguyên được yêu cầu ở định dạng HTTP-date: Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT. Client (trình duyệt, ứng dụng di động, proxy) lưu ngày này cùng với tài nguyên đã lưu trong bộ nhớ đệm. Khi yêu cầu lại, client gửi header If-Modified-Since với cùng ngày, và máy chủ so sánh nó với thời gian sửa đổi hiện tại của tài nguyên.

Giao thức yêu cầu có điều kiện với Last-Modified được định nghĩa trong RFC 7232 và được tất cả các máy chủ HTTP hiện đại hỗ trợ. Định dạng ngày được quy định chặt chẽ — chỉ GMT (Greenwich Mean Time) mà không có chỉ định múi giờ. Máy chủ phải trả về ngày ở ba định dạng có thể: RFC 1123 (tiêu chuẩn), RFC 850 (cũ) hoặc ANSI C asctime. Trong thực tế, hầu hết các máy chủ đều sử dụng định dạng RFC 1123 với độ dài cố định 29 ký tự.

Last-Modified thuộc danh mục cơ chế xác thực bộ nhớ đệm: nó không cho client biết liệu phản hồi có thể được lưu vào bộ nhớ đệm hay không, mà cung cấp công cụ để kiểm tra tính hợp lệ của tài nguyên đã được lưu vào bộ nhớ đệm. Chính sách lưu trữ đệm được xác định riêng qua header Cache-Control. Theo một nghiên cứu của Akamai (2025), việc cấu hình đúng Last-Modified cùng với Cache-Control giảm tải trên các máy chủ nguồn lên tới 70% cho nội dung tĩnh.

Last-Modified xuất hiện khi nào?

Header Last-Modified được định nghĩa từ HTTP/1.0 (RFC 1945, 1996) và trở thành một trong những cơ chế quản lý bộ nhớ đệm đầu tiên trên web. Trước khi ETag xuất hiện trong HTTP/1.1, nó là cách duy nhất để thực hiện các yêu cầu có điều kiện. Mặc dù đã cũ, header này vẫn còn phù hợp nhờ sự đơn giản của nó — máy chủ không cần tính toán hash nội dung, chỉ cần đọc dấu thời gian tệp từ hệ thống tệp hoặc trường updated_at từ cơ sở dữ liệu.

Last-Modified hoạt động thế nào?

Chu kỳ đầy đủ bao gồm ba giai đoạn. Ở yêu cầu đầu tiên, máy chủ trả về tài nguyên với header Last-Modified và trạng thái HTTP 200 OK. Client lưu phản hồi vào bộ nhớ đệm cùng với ngày. Khi yêu cầu lại, client gửi header If-Modified-Since với ngày đã lưu. Máy chủ so sánh ngày này với thời gian sửa đổi hiện tại của tài nguyên. Nếu tài nguyên không thay đổi — trả về 304 Not Modified với phần thân rỗng. Nếu đã thay đổi — 200 OK với dữ liệu mới và Last-Modified mới.

http
// Yêu cầu đầu tiên — máy chủ trả về tài nguyên cùng ngày
HTTP/1.1 200 OK
Content-Type: application/json
Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT

[{"id": 1, "name": "Alice"}]

// Yêu cầu lại — client gửi ngày đã lưu
GET /api/users HTTP/1.1
Host: example.com
If-Modified-Since: Wed, 02 Jul 2025 14:30:00 GMT

// Phản hồi — dữ liệu không thay đổi
HTTP/1.1 304 Not Modified

Đối với ứng dụng di động, Last-Modified đặc biệt hữu ích cho việc đồng bộ dữ liệu. Ứng dụng lưu ngày cập nhật thành công cuối cùng và gửi nó đến máy chủ trong If-Modified-Since. Nếu có thêm dữ liệu hoặc dữ liệu đã thay đổi — máy chủ trả về tập đầy đủ. Nếu không — 304, và ứng dụng sử dụng bản sao địa phương. OkHttp và URLSession hỗ trợ cơ chế này tự động thông qua các hệ thống lưu trữ đệm tích hợp.

Máy chủ xác định ngày như thế nào?

Đối với tệp tĩnh, Nginx và Apache lấy ngày từ thuộc tính hệ thống tệp — mtime (thời gian sửa đổi). Đối với nội dung động, code máy chủ phải đặt Last-Modified một cách tường minh dựa trên logic nghiệp vụ: trường updated_at từ cơ sở dữ liệu, ngày commit cuối cùng trong Git, dấu thời gian của artifact xây dựng. Nếu Last-Modified không được đặt tường minh, máy chủ có thể không gửi header này, và client sẽ không thể thực hiện các yêu cầu có điều kiện theo ngày.

Last-Modified so với ETag

Last-Modified và ETag thực hiện nhiệm vụ tương tự — cho phép client kiểm tra tính hợp lệ của bộ nhớ đệm — nhưng có sự khác biệt cơ bản. Last-Modified sử dụng dấu thời gian, ETag sử dụng định danh phiên bản duy nhất. Mỗi cách tiếp cận có các kịch bản riêng nơi nó hiệu quả hơn, và đặc tả HTTP khuyên nghị sử dụng cả hai header cùng nhau.

Tiêu chíLast-ModifiedETag
Bản chấtNgày sửa đổi lần cuốiĐịnh danh phiên bản duy nhất
Độ chính xácĐến giâyĐến bit (hash)
Độ phức tạp triển khaiThấp — tự động từ hệ thống tệpTrung bình — cần tính toán hash
Máy chủ cụmVấn đề: mtime có thể khác nhau giữa các nútỔn định với dữ liệu giống nhau giữa các nút
Hỗ trợ phạm viKhông ảnh hưởng đến yêu cầu RangeYêu cầu ETag mạnh cho phạm vi
Khuyên nghịCho tệp tĩnh và API đơn giảnCho API nơi cần kiểm tra chính xác

Lợi thế chính của Last-Modified là sự đơn giản. Máy chủ không cần tính toán hash nội dung, tiết kiệm tài nguyên CPU cho mỗi yêu cầu. Đối với các dự án có lưu lượng truy cập cao phục vụ tệp tĩnh hoặc dữ liệu có dấu thời gian rõ ràng, Last-Modified vẫn là lựa chọn tối ưu. Mặt khác, ETag cung cấp độ chính xác tuyệt đối — thay đổi một ký tự trong phản hồi JSON sẽ thay đổi ETag, nhưng có thể không thay đổi ngày (nếu tệp bị ghi đè với cùng phiên bản).

Sử dụng cùng nhau

Đặc tả khuyên nghị trả về cả hai header cùng một lúc. Máy chủ bao gồm cả Last-Modified và ETag trong phản hồi 200 OK. Client gửi cả hai header có điều kiện — If-Modified-Since và If-None-Match. Máy chủ kiểm tra ETag trước (có ưu tiên), sau đó Last-Modified. Nếu ít nhất một header báo hiệu sự thay đổi — phản hồi đầy đủ được trả về. Điều này mang lại sự linh hoạt tối đa: ETag đảm bảo độ chính xác, Last-Modified cung cấp kiểm tra dự phòng cho các client không hỗ trợ ETag.

Cấu hình Last-Modified trên máy chủ

Việc cấu hình Last-Modified phụ thuộc vào loại máy chủ. Đối với Nginx và Apache, Last-Modified được đặt tự động cho tệp tĩnh dựa trên mtime. Đối với ứng dụng động, header phải được đặt trong code máy chủ. Hãy xem cấu hình trên các nền tảng phổ biến.

javascript
// Express.js — thiết lập Last-Modified
app.get("/api/users", async (req, res) => {
    const updatedAt = await getLastUpdate()
    const ifModifiedSince = req.get("If-Modified-Since")

    // Kiểm tra If-Modified-Since
    if (ifModifiedSince && new Date(ifModifiedSince)
        >= updatedAt) {
        return res.status(304).end()
    }

    const users = await getUsers()
    res.set("Last-Modified", updatedAt.toUTCString())
    res.json(users)
})

Trong ví dụ Express.js, máy chủ lấy ngày cập nhật dữ liệu cuối cùng từ cơ sở dữ liệu, kiểm tra If-Modified-Since từ client, và nếu bộ nhớ đệm vẫn còn mới — trả về 304. Nếu dữ liệu đã thay đổi — đặt Last-Modified mới và trả về phản hồi đầy đủ. toUTCString() chuyển đổi ngày sang định dạng HTTP yêu cầu. Trong môi trường sản xuất, nên lưu updatedAt vào bộ nhớ đệm Redis để tránh truy vấn cơ sở dữ liệu mỗi lần.

Nginx: cấu hình Last-Modified

Nginx tự động đặt Last-Modified cho tệp tĩnh dựa trên thời gian sửa đổi lần cuối của tệp. Có thể tắt hoặc thay đổi hành vi này bằng chỉ thị etag (tắt ETag) hoặc qua module ngx_http_headers_module. Đối với các yêu cầu proxy đến backend, Last-Modified được chuyển từ phản hồi upstream mà không thay đổi. Quan trọng: nếu backend không trả về Last-Modified, Nginx sẽ không tự động thêm nó cho các phản hồi động.

Hạn chế và rẫy ro

Last-Modified có một số hạn chế đã biết. Chính là độ chính xác đến giây. Nếu một tài nguyên thay đổi hai lần trong một giây, client có thể bỏ lỡ phiên bản mới. Trong thực tế, đây là kịch bản hiếm, nhưng đối với các bản cập nhật tần suất cao (nguồn cấp dữ liệu ticker, trò chuyện), ETag được khuyên dùng. Hạn chế thứ hai là vấn đề cụm: trên các máy chủ khác nhau, mtime của tệp có thể khác nhau do sao chép hoặc triển khai, khiến Last-Modified không nhất quán.

Hạn chế thứ ba — việc xử lý If-Modified-Since với độ chính xác đến giây có thể dẫn đến các yêu cầu không cần thiết khi thăm dò máy chủ thường xuyên. Nếu client gửi If-Modified-Since mỗi 500 ms, máy chủ trả về 200 OK mỗi lần vì ngày không thay đổi, nhưng tài nguyên thực tế đã được cập nhật. Giải pháp là sử dụng kết hợp với ETag: ETag sẽ phát hiện thay đổi trong vòng một giây, trong khi Last-Modified vẫn là dự phòng.

Vấn đề thứ tư — Last-Modified không phân biệt giữa các phiên bản khác nhau của cùng một tài nguyên có cùng ngày. Nếu một tệp được khôi phục từ bản sao lưu và mtime của nó trùng với bản gốc, client sẽ không nhận thấy nội dung đã thay đổi. ETag giải quyết vấn đề này: hash nội dung chắc chắn sẽ thay đổi khi có bất kỳ thay đổi dữ liệu nào, bất kể dấu thời gian. Đối với dữ liệu quan trọng, luôn sử dụng cả hai header.

  • Độ chính xác đến giây — không phát hiện thay đổi trong vòng một giây; sử dụng ETag cho các bản cập nhật tần suất cao
  • Cụm — mtime có thể khác nhau giữa các máy chủ; đồng bộ qua NTP hoặc sử dụng ETag
  • Điều kiện cạnh tranh — nếu tài nguyên thay đổi sau khi gửi If-Modified-Since nhưng trước khi máy chủ kiểm tra
  • Proxy hiểu sai — một số proxy có thể thay đổi Last-Modified khi lưu vào bộ nhớ đệm; HTTPS giải quyết vấn đề này

Câu hỏi thường gặp

Định dạng ngày nào được sử dụng trong Last-Modified?

Chỉ GMT (Greenwich Mean Time) ở định dạng RFC 1123: thứ trong tuần, ngày, tháng, năm, giờ:phút:giây. Ví dụ: Wed, 02 Jul 2025 14:30:00 GMT. Múi giờ luôn là GMT, các định dạng khác không được chấp nhận.

Last-Modified có thể ở tương lai không?

Về mặt kỹ thuật có thể, nhưng điều này vi phạm RFC 7232. Nếu máy chủ trả về ngày trong tương lai, client sẽ không cập nhật tài nguyên cho đến khi ngày đó đến. Cấu hình như vậy được coi là lỗi — ngày phải ở quá khứ hoặc hiện tại.

Last-Modified có hoạt động với yêu cầu POST không?

Không, các yêu cầu có điều kiện If-Modified-Since chỉ hoạt động với GET và HEAD. Yêu cầu POST không được lưu vào bộ nhớ đệm và không sử dụng xác thực theo ngày. Để kiểm tra tính mới trong POST, hãy sử dụng ETag hoặc cơ chế tùy chỉnh.

Last-Modified tương tác với Cache-Control như thế nào?

Cache-Control xác định chính sách lưu trữ đệm (thời gian lưu tối đa, ai có thể lưu), trong khi Last-Modified là cơ chế xác thực cho bộ nhớ đệm hết hạn. Sau khi max-age hết hạn, client gửi If-Modified-Since để kiểm tra tính mới.

Làm gì nếu Last-Modified không thay đổi khi dữ liệu được cập nhật?

Kiểm tra xem máy chủ có đặt header từ nguồn chính xác — cơ sở dữ liệu, hệ thống tệp hoặc API — hay không. Đối với phản hồi động, hãy đảm bảo bạn gọi rõ ràng res.setHeader(“Last-Modified”, ...) trong code xử lý.

Tổng kết

  • Last-Modified — HTTP header chứa ngày sửa đổi lần cuối của tài nguyên cho yêu cầu có điều kiện 304
  • Triển khai đơn giản — tự động hoạt động cho tệp tĩnh (mtime) và yêu cầu code tối thiểu cho API
  • Độ chính xác đến giây — hạn chế chính; cho thay đổi tần suất cao, hãy sử dụng ETag
  • ETag chính xác hơn, Last-Modified đơn giản hơn — kết hợp tối ưu: cả hai header cùng nhau
  • Định dạng ngày HTTP — chỉ GMT, RFC 1123, độ dài cố định 29 ký tự
  • Cụm — yêu cầu đồng bộ thời gian (NTP) hoặc sử dụng ETag làm cơ chế chính
  • Khuyên nghị — luôn thêm Last-Modified cho API và kích hoạt cho tệp tĩnh qua Nginx/Apache

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.

Thảo luận dự án

Đọc thêm