Developer Playground
Cache Control & ETags: Optimizing Web Performance
Table of Contents
What is Web Caching?
Web caching is a technique that stores copies of resources to serve future requests more quickly:
- It reduces bandwidth consumption and server load
- It improves page load times for returning visitors
- It occurs at various levels: browser, CDN, proxy, and server
- Effective caching strategies balance freshness with performance
Cache-Control Header
The Cache-Control header is the primary mechanism for defining caching policies in HTTP/1.1:
- It's sent by the server in HTTP responses to dictate how the resource should be cached
- It can also be sent in requests by clients to specify caching preferences
- It consists of one or more directives separated by commas
- Each directive controls a different aspect of caching behavior
Example: Cache-Control: max-age=3600, must-revalidate
Common Cache Directives
Cache-Control directives define the specific behavior of caches:
Cacheability
- public - Response can be stored by any cache
- private - Response can only be stored in private cache (e.g., browser)
- no-store - Response must not be stored in any cache
- no-cache - Cache can store the response but must revalidate before use
Expiration
- max-age=seconds - Maximum time the resource is considered fresh
- s-maxage=seconds - Like max-age but only for shared caches
- stale-while-revalidate=seconds - Allows serving stale content while revalidating
- stale-if-error=seconds - Allows serving stale content if revalidation fails
Revalidation and Reloading
- must-revalidate - Cache must verify stale resources before use
- proxy-revalidate - Like must-revalidate but only for shared caches
- immutable - Resource will not change; avoid revalidation
What are ETags?
ETags (Entity Tags) provide a mechanism for cache validation:
- ETags are unique identifiers assigned to specific versions of resources
- They're included in the HTTP response headers from the server
- Unlike time-based validation, ETags can detect changes regardless of timing
- They're particularly useful when resources change unpredictably
- ETags can be based on content hashes, timestamps, or version identifiers
Example: ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
ETag Workflow
ETags enable conditional requests and efficient revalidation:
Strong vs. Weak ETags
ETags come in two flavors with different validation guarantees:
- Strong ETags guarantee byte-for-byte identity
- Format:
ETag: "abc123"
- Used when exact equality is required
- Appropriate for range requests and full downloads
- Weak ETags indicate semantic equivalence but not byte equality
- Format:
ETag: W/"abc123"
(note the W/ prefix) - Used when content may vary slightly but remains functionally equivalent
- Good for content that includes timestamps, view counts, or other minor changing elements
Best Practices
Optimize your caching strategy with these guidelines:
Resource Type-Based Caching
- Static assets (JS, CSS, images) - Long cache times with versioned URLs or ETags
- HTML content - Shorter cache times with ETags for validation
- API responses - Cache-Control based on data volatility
- User-specific content - private directive with appropriate validation
ETag Implementation
- Generate ETags based on content hashes for accurate change detection
- Use weak ETags for content with minor, inconsequential variations
- Ensure ETag generation is consistent across server instances
- Combine ETags with appropriate Cache-Control directives
Common Patterns
- Immutable content:
Cache-Control: max-age=31536000, immutable
- Regularly updated content:
Cache-Control: max-age=3600
with ETags - Dynamic but cacheable API:
Cache-Control: private, max-age=0, must-revalidate
with ETags - Uncacheable content:
Cache-Control: no-store
ETag Implementation Example (Spring Boot 3)
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.HttpStatus;
import org.springframework.http.CacheControl;
import org.springframework.web.context.request.WebRequest;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HexFormat;
@RestController
@RequestMapping("/api")
public class DataController {
@GetMapping("/data")
public ResponseEntity<Map<String, Object>> getData(WebRequest webRequest) {
// Create response data
Map<String, Object> data = Map.of(
"message", "Hello World",
"timestamp", Instant.now().toEpochMilli()
);
// Generate ETag from content
String jsonData = data.toString();
String etag = generateETag(jsonData);
// Check if the request has a matching ETag
if (webRequest.checkNotModified(etag)) {
return ResponseEntity.status(HttpStatus.NOT_MODIFIED)
.eTag(etag)
.build();
}
// Return response with ETag and Cache-Control
return ResponseEntity.ok()
.eTag(etag)
.cacheControl(CacheControl.maxAge(3600, TimeUnit.SECONDS))
.body(data);
}
private String generateETag(String content) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] hash = digest.digest(content.getBytes(StandardCharsets.UTF_8));
return HexFormat.of().formatHex(hash);
} catch (Exception e) {
throw new RuntimeException("Failed to generate ETag", e);
}
}
}
Conclusion
Effective caching with Cache-Control headers and ETags is essential for optimizing web performance. By implementing these mechanisms properly, you can:
- Reduce bandwidth usage and server load
- Improve page load times for returning visitors
- Ensure content freshness while maximizing cache efficiency
- Create a better user experience with faster, more responsive websites
Remember that caching is a balance between performance and freshness. The right strategy depends on your specific use case, content volatility, and user expectations.
Advertisement