Last-Modified — essence, mechanism and configuration of the modification date header

Author: IT Sectr Published: 2026-03-09 Reading time: 9 min

Last-Modified is an HTTP response header that indicates the date and time a resource was last modified on the server, allowing the client to make conditional requests via If-Modified-Since. If the resource has not changed since the specified date, the server returns 304 Not Modified without sending the response body, which significantly saves bandwidth. According to RFC 7232 (IETF, 2014), conditional requests with Last-Modified reduce page load time by 30-60% on repeat visits. The header is automatically supported by most HTTP servers and proxies.

Key Takeaways

  • Last-Modified — an HTTP header with the date of the last resource modification for If-Modified-Since conditional requests
  • 304 Not Modified — the server’s response if the resource has not changed; the client uses its cached copy
  • Second precision — a header limitation: changes within one second may go undetected
  • Works alongside ETag — the server returns both headers, the client sends both conditional requests
  • Automatic generation — Nginx and Apache set Last-Modified for static files from the file system

What is Last-Modified?

Last-Modified is an HTTP header that belongs to the group of conditional request headers. The server adds it to a response to GET or HEAD, indicating the date and time of the last modification of the requested resource in HTTP-date format: Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT. The client (browser, mobile app, proxy) stores this date together with the cached resource. On a subsequent request, the client sends the If-Modified-Since header with the same date, and the server compares it with the current modification time of the resource.

The conditional request protocol with Last-Modified is defined in RFC 7232 and is supported by all modern HTTP servers. The date format is strictly regulated — only GMT (Greenwich Mean Time) without time zone indication. The server must return the date in three possible formats: RFC 1123 (standard), RFC 850 (obsolete), or ANSI C asctime. In practice, almost all servers use RFC 1123 format with a fixed length of 29 characters.

Last-Modified belongs to the category of validation caching mechanisms: it does not tell the client whether the response can be cached, but provides a tool for checking the freshness of an already cached resource. Caching policy is set separately via the Cache-Control header. According to a study by Akamai (2025), proper configuration of Last-Modified together with Cache-Control reduces origin server load by up to 70% for static content.

When Did Last-Modified Appear

The Last-Modified header was defined back in HTTP/1.0 (RFC 1945, 1996) and became one of the first caching management mechanisms on the web. Before ETag appeared in HTTP/1.1, it was the only way to perform conditional requests. Despite its age, the header remains relevant due to its simplicity — the server does not need to compute a content hash, it only needs to read the file timestamp from the file system or the updated_at field from the database.

How Does Last-Modified Work?

The full cycle consists of three stages. On the first request, the server returns the resource with the Last-Modified header and HTTP status 200 OK. The client caches the response together with the date. On a repeat request, the client sends the If-Modified-Since header with the stored date. The server compares this date with the current modification time of the resource. If the resource has not changed — it returns 304 Not Modified with an empty body. If it has changed — 200 OK with new data and a new Last-Modified.

http
// First request — server returns the resource with a date
HTTP/1.1 200 OK
Content-Type: application/json
Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT

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

// Repeat request — client sends the stored date
GET /api/users HTTP/1.1
Host: example.com
If-Modified-Since: Wed, 02 Jul 2025 14:30:00 GMT

// Response — data has not changed
HTTP/1.1 304 Not Modified

For mobile applications, Last-Modified is especially useful for data synchronization. The application stores the date of the last successful update and sends it to the server in If-Modified-Since. If there is more data or it has changed — the server returns the full set. If not — 304, and the application uses the local copy. OkHttp and URLSession support this mechanism automatically through built-in caching systems.

How the Server Determines the Date

For static files, Nginx and Apache take the date from file system attributes — mtime (modification time). For dynamic content, the server code must explicitly set Last-Modified based on business logic: the updated_at field from the database, the date of the last Git commit, the build artifact timestamp. If Last-Modified is not explicitly set, the server may not send the header at all, and the client will not be able to make conditional requests by date.

Last-Modified vs ETag

Last-Modified and ETag perform a similar task — allowing the client to check cache freshness — but have fundamental differences. Last-Modified uses a timestamp, ETag uses a unique version identifier. Each approach has its own scenarios where it is more effective, and the HTTP specification recommends using both headers together.

CriterionLast-ModifiedETag
EssenceDate of last modificationUnique version identifier
PrecisionDown to a secondDown to a bit (hash)
Implementation complexityLow — automatic from file systemMedium — requires hash computation
Clustered serversIssue: mtime may differ across nodesStable with identical data across nodes
Range supportDoes not affect Range requestsRequires strong ETag for ranges
RecommendationFor static files and simple APIsFor APIs where precise checks matter

The main advantage of Last-Modified is simplicity. The server does not need to compute a content hash, which saves CPU resources on each request. For high-traffic projects serving static files or data with clear timestamps, Last-Modified remains the optimal choice. ETag, on the other hand, provides absolute precision — changing a single letter in a JSON response will change the ETag, but may not change the date (if the file was overwritten with the same version).

Using Both Together

The specification recommends returning both headers simultaneously. The server includes both Last-Modified and ETag in the 200 OK response. The client sends both conditional headers — If-Modified-Since and If-None-Match. The server checks ETag first (it has priority), then Last-Modified. If at least one signals a change — the full response is returned. This provides maximum flexibility: ETag ensures precision, Last-Modified provides a fallback check for clients that do not support ETag.

Configuring Last-Modified on the Server

Configuring Last-Modified depends on the server type. For Nginx and Apache, Last-Modified is set automatically for static files based on mtime. For dynamic applications, the header must be set in the server code. Let’s look at configuration on popular platforms.

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

    // Checking 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)
})

In the Express.js example, the server gets the date of the last data update from the database, checks If-Modified-Since from the client, and if the cache is still fresh, returns 304. If the data has changed — it sets a new Last-Modified and returns the full response. toUTCString() converts the date to the required HTTP format. In production, you should cache updatedAt in Redis to avoid querying the database on every request.

Nginx: Configuring Last-Modified

Nginx automatically sets Last-Modified for static files based on the file’s last modification time. You can disable or change this behavior using the etag directive (disabling ETag) or through the ngx_http_headers_module. For proxied requests to the backend, Last-Modified is passed from the upstream response unchanged. Important: if the backend does not return Last-Modified, Nginx will not add it automatically for dynamic responses.

Limitations and Pitfalls

Last-Modified has several known limitations. The main one is second precision. If a resource changes twice within one second, the client may miss the new version. In practice this is a rare scenario, but for high-frequency updates (ticker feeds, chats) ETag is recommended. The second limitation is the clustering problem: on different servers a file may have different mtime values due to copying or deployment, making Last-Modified inconsistent.

The third limitation — handling If-Modified-Since with second precision can lead to unnecessary requests when polling the server frequently. If the client sends If-Modified-Since every 500 ms, the server returns 200 OK each time because the date hasn’t changed, but the resource has actually already been updated. The solution is to use a combination with ETag: ETag will catch the change within a second, while Last-Modified remains as a fallback.

The fourth issue — Last-Modified does not distinguish between different versions of the same resource with the same date. If a file is restored from a backup and its mtime matches the original, the client will not notice that the content has changed. ETag solves this problem: the content hash will guaranteed change with any data modification, regardless of the timestamp. For critical data, always use both headers.

  • Second precision — does not catch changes within one second; use ETag for high-frequency updates
  • Clustering — mtime may differ across servers; synchronize via NTP or use ETag
  • Race condition — if the resource changes after sending If-Modified-Since but before the server check
  • Proxy misinterpretation — some proxies may alter Last-Modified when caching; HTTPS solves this

Frequently Asked Questions

What date format is used in Last-Modified?

Only GMT (Greenwich Mean Time) in RFC 1123 format: day of the week, day, month, year, hours:minutes:seconds. Example: Wed, 02 Jul 2025 14:30:00 GMT. The time zone is always GMT, other formats are not allowed.

Can Last-Modified be in the future?

Technically yes, but it violates RFC 7232. If the server returns a date in the future, clients will not update the resource until that date arrives. Such a configuration is considered an error — the date must be in the past or present.

Does Last-Modified work with POST requests?

No, If-Modified-Since conditional requests only work with GET and HEAD. POST requests are not cached and do not use date-based validation. For POST freshness checks, use ETag or custom mechanisms.

How does Last-Modified interact with Cache-Control?

Cache-Control defines caching policy (maximum storage time, who can cache), while Last-Modified is a validation mechanism for expired cache. After max-age expires, the client sends If-Modified-Since to check freshness.

What if Last-Modified does not change when data is updated?

Check that the server is actually setting the header from the correct source — database, file system, or API. For dynamic responses, make sure you are explicitly calling res.setHeader(“Last-Modified”, ...) in the handler code.

Summary

  • Last-Modified — an HTTP header with the date of the last resource modification for 304 conditional requests
  • Simple implementation — works automatically for static files (file mtime) and requires minimal code for APIs
  • Second precision — the main limitation; for high-frequency changes use ETag
  • ETag is more precise, Last-Modified is simpler — optimal combination: both headers together
  • HTTP date format — only GMT, RFC 1123, fixed length of 29 characters
  • Clustering — requires time synchronization (NTP) or using ETag as the primary mechanism
  • Recommendation — always add Last-Modified for APIs and enable it for static files via Nginx/Apache

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also