API Caching with ETag and Last-Modified

Khimananda Oli 8 min read Programming and Languages
API Caching with ETag and Last-Modified

By Khimananda Oli | Last reviewed: August 2026

High-latency APIs and excessive egress costs often stem from redundant data transfer rather than slow backend logic. Implementing API caching with ETag and Last-Modified headers allows clients to validate cached responses without re-downloading identical payloads, solving this bottleneck at the protocol level. This guide covers the exact implementation patterns, validation logic, and common pitfalls for production-grade conditional requests.

Conditional Request LifecycleClient (Browser)Origin ServerGET /api/resource200 OK + ETag: "v1" + BodyGET /api/resourceIf-None-Match: "v1"304 Not Modified (No Body)Client serves cached copy instantly
HTTP conditional request flow demonstrating API caching with ETag validation and 304 responses

How does API caching with ETag and Last-Modified actually work?

The mechanism relies on HTTP/1.1 validators defined in RFC 9110. When a server responds to a GET request, it includes an ETag (entity tag) or Last-Modified header representing that specific version of the resource. The client stores both the response body and these validators in its cache. On subsequent requests for the same URL, the client sends the validator back via If-None-Match (for ETags) or If-Modified-Since (for timestamps).

The server compares the incoming validator against the current resource state. If they match, the server returns a 304 Not Modified status with no message body. This saves bandwidth, reduces serialization overhead, and lowers latency significantly. For teams managing high-traffic endpoints, understanding this handshake is foundational to application-level caching strategies that complement network-layer optimizations.

A common mistake is treating ETags as simple cache keys. They are opaque validators. A weak ETag (W/"abc") indicates semantic equivalence but not byte-for-byte identity, suitable for dynamic content where minor formatting differences are acceptable. Strong ETags ("abc") guarantee bit-level equality and are required for range requests or resumable downloads. Choosing the wrong type breaks partial content delivery or causes false cache hits.

How do you implement ETag generation in backend applications?

Generating reliable ETags requires a deterministic function of the resource's state. Avoid hashing the entire response body on every request; this defeats the purpose by forcing full computation before validation. Instead, derive tags from metadata that changes only when the content changes.

Database-driven versioning

For CRUD resources, combine the primary key, updated_at timestamp, and a version counter. In PostgreSQL or MySQL, this looks like:

<?php
// Laravel example: Efficient ETag generation
$resource = Product::find($id);
$etag = hash('xxh3', implode('-', [
    $resource->id,
    $resource->updated_at->timestamp,
    $resource->version
]));

return response()->json($resource)
    ->setEtag($etag)
    ->setLastModified($resource->updated_at);

This approach ensures the ETag changes exactly when the underlying data mutates. The xxh3 hash is preferred over MD5 or SHA-256 for ETags because it is non-cryptographic and significantly faster, reducing CPU overhead during high-throughput validation checks.

Content-addressable storage

For static assets or immutable blobs, compute the hash once at upload time and store it as metadata. S3 objects, for instance, already provide an ETag based on the MD5 of the content (for non-multipart uploads). Re-computing this on every read is wasteful. Store the hash in your database or object metadata and serve it directly.

  • Deterministic: Same input always produces the same ETag.
  • Collision-resistant: Different states must produce different ETags.
  • Efficient: Computation cost must be lower than serializing the full response.
  • Opaque: Clients should never parse or depend on ETag structure.

When should you use Last-Modified instead of ETags?

Last-Modified provides second-level granularity. It is simpler to implement and debug because humans can read timestamps. However, it fails when multiple updates occur within the same second or when filesystem timestamps are unreliable (common in containerized environments or distributed storage).

Use Last-Modified as a fallback validator alongside ETags, not as a replacement. HTTP/1.1 specifies that if both If-None-Match and If-Modified-Since are present, the server MUST ignore If-Modified-Since unless the ETag comparison yields a match. This precedence prevents stale timestamp comparisons from overriding precise entity validation.

Validator Selection LogicResource Changed?Sub-second precision needed?Only timestamp available?YesYesUse Strong ETag(Hash of content/version)Use Last-Modified(Second granularity only)Supports Range RequestsLegacy Client CompatAlways prefer ETag + Last-Modified togetherServer ignores If-Modified-Since if If-None-Match is present
Decision framework for choosing between ETag and Last-Modified validators in API caching

In practice, I recommend always emitting both headers when possible. This maximizes compatibility with older clients and intermediate proxies that may strip or mishandle one validator type. Monitoring tools like those discussed in metrics fundamentals should track 304 hit rates separately for ETag vs. Last-Modified to identify validation inefficiencies.

What are the common pitfalls in conditional request handling?

Misimplementing validators causes subtle bugs that are hard to diagnose in production. These are the most frequent issues I encounter during architecture reviews:

  1. Vary header omission: If your response differs based on Accept-Encoding, Authorization, or custom headers, you MUST include them in the Vary response header. Without this, caches may serve a compressed response to a client that doesn't support gzip, or leak private data across users. The ETag alone does not account for representation variance.
  2. Weak ETag misuse: Weak validators cannot be used for range requests. If your API supports partial content (206 Partial Content), you must use strong ETags. Returning a weak ETag with Accept-Ranges violates the spec and breaks download managers.
  3. Clock skew in Last-Modified: Distributed systems often have unsynchronized clocks. A file modified on Server A at 10:00:01 might appear older than a cached copy timestamped 10:00:00 on Server B due to NTP drift. Always use UTC and ensure NTP synchronization across all nodes generating timestamps.
  4. Ignoring If-Match for writes: Conditional PUT/PATCH requests use If-Match to prevent lost updates. Many APIs implement GET validation but skip write concurrency control. This leads to race conditions where two clients overwrite each other's changes silently.
  5. Over-validation: Generating ETags by hashing the full JSON response on every request adds CPU cost that may exceed the savings from avoided transfers. Profile your validation logic. For large payloads, consider structural hashing (only hash changed fields) or version counters.

Security-sensitive endpoints require extra caution. Never include sensitive data in ETag computation if the tag itself could leak information. While ETags are typically hashes, predictable patterns can sometimes reveal resource existence or modification frequency. Review your threat model alongside your secrets management strategy to ensure validators don't become side channels.

How do ETag and Last-Modified compare for modern API architectures?

CriteriaETagLast-Modified
PrecisionByte-level or semantic (strong/weak)Second-level granularity
Computation CostVariable (hash function dependent)Negligible (metadata lookup)
Distributed SystemsSafe (content-derived)Risky (clock skew)
Range RequestsRequired (strong only)Insufficient alone
Human DebuggingOpaque stringReadable timestamp
Client SupportUniversal (HTTP/1.1+)Universal (legacy compatible)
Best ForDynamic content, APIs, binariesStatic files, legacy systems

Modern REST and GraphQL APIs should default to strong ETags derived from application state. Reserve Last-Modified for static asset pipelines or backward compatibility layers. The performance difference is negligible compared to the correctness guarantees ETags provide in distributed environments.

Validation Caching ArchitectureClient CacheStores ETag + BodyCDN / Reverse ProxyValidates w/o OriginAPI GatewayRate Limit + AuthApplication ServerGenerates ETagCache Hit (304)Zero origin loadGateway ValidationAuth check onlyFull ComputeDB + SerializationLayered Validation Reduces Origin Load by 60-90%Each layer handles 304 independently using stored validators
Multi-layer API caching architecture showing validation points from client to origin server

Implementing API Caching with ETag and Last-Modified in Production

Correct implementation of API caching with ETag and Last-Modified transforms API performance by shifting work from computation to comparison. Start by auditing your highest-traffic read endpoints. Add ETag generation based on resource versioning, emit Last-Modified as a secondary validator, and configure your reverse proxy to respect conditional requests. Monitor 304 ratios to validate effectiveness.

Remember that caching is a contract between client and server. Violating HTTP semantics breaks that contract and causes data inconsistency. Test thoroughly with real clients, not just curl scripts. Browser caches, mobile SDKs, and CDN edge nodes all behave differently under edge cases.

If you need help designing a caching strategy that aligns with your compliance requirements or infrastructure constraints, reach out to discuss your architecture. Proper validation caching pays for itself in reduced cloud bills and improved user experience within weeks of deployment.

Frequently Asked Questions

ETag uses a unique hash or version string to validate content changes precisely. Last-Modified relies on file timestamps which have one-second granularity. ETags prevent false positives when files are touched without actual content modification, making them more reliable for API caching validation in 2026.

Use the response etag method with true as the second parameter to generate strong validators based on response content hash. Strong ETags require byte-for-byte equality, unlike weak validators that allow semantic equivalence. This ensures strict cache validation for JSON API responses serving critical data.

Yes, combining both provides fallback validation mechanisms. Clients send If-None-Match and If-Modified-Since headers simultaneously. Servers prioritize ETag comparison but fall back to timestamp checks if ETags are missing. This dual approach maximizes compatibility across diverse HTTP clients and legacy proxy infrastructure.

Check middleware execution order and ensure cache validation occurs before response generation. Verify ETag values match exactly between requests. Confirm clients send proper conditional headers like If-None-Match. Debug using curl verbose mode to inspect request and response header exchanges during validation failures.

Weak ETags work for semantic equivalence where representation format may vary slightly. Avoid them for binary downloads or checksum-dependent workflows. Most JSON APIs benefit from strong ETags since content must match exactly. Use weak validators only when minor formatting differences do not affect client behavior.

Nginx automatically generates ETags for static files using size and modification time by default. Disable this with etag off directive if backend generates custom validators. For proxied API responses, Nginx passes through origin ETags unchanged unless explicitly configured to override or strip validation headers.

Major CDNs honor these validators at edge locations, reducing origin load significantly. Configure cache-control directives to enable revalidation. Some providers require explicit settings to forward conditional headers to origin. Test with provider-specific tools to verify edge nodes properly return 304 responses during validation cycles.

Build processes often regenerate assets with new hashes even when content remains identical. Timestamp-based ETags change during redeployment regardless of content. Switch to content-addressable hashing strategies. Ensure consistent serialization ordering for JSON responses to maintain stable ETags across application restarts and horizontal scaling events.

Conditional requests return minimal 304 responses without payload bodies when content is unchanged. This eliminates redundant data transfer for frequently polled endpoints. Bandwidth savings compound across high-traffic APIs where most requests hit cached versions. Monitor origin egress metrics to quantify cost reductions from effective validation caching.

No, authenticated endpoints benefit from validation caching to reduce server processing. Set private cache-control directives to prevent shared caches from storing responses. User-specific ETags still enable efficient browser caching. Never expose sensitive data in ETag values themselves, as headers are visible to intermediaries and logs.

Use curl with -H If-None-Match header containing previously received ETag value. Expect 304 status code with empty body on match. Browser DevTools Network tab shows conditional request details. Write integration tests asserting 304 responses for unchanged resources and 200 responses after deliberate content modifications.

Redis stores computed ETags alongside cached payloads to avoid regenerating hashes on every request. This reduces CPU overhead for expensive serialization operations. Use short TTLs matching your data freshness requirements. Hash keys should include relevant query parameters to ensure distinct ETags for filtered or paginated API responses.

Clock skew causes premature cache invalidation or stale content delivery. Server and client time differences exceeding one second break timestamp validation. Prefer ETags for distributed systems where clock synchronization is unreliable. If using Last-Modified, implement grace periods and monitor NTP health across all application servers.

Compression changes response bytes, requiring different ETags for compressed versus uncompressed variants. Servers must track both representations separately. Vary header must include Accept-Encoding to signal this distinction. Misconfigured compression with single ETags causes cache corruption where clients receive wrong encoding format during conditional request validation.

Yes, ETag checks typically involve lightweight hash comparisons or metadata lookups rather than expensive joins and aggregations. Validation occurs before business logic execution when properly implemented. This prevents unnecessary database load for unchanged resources. Profile your validation layer to ensure it remains sub-millisecond under production traffic volumes.