Developer Playground

Cache Control & ETags: Optimizing Web Performance

Updated: May 1, 2025

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
Web Caching Overview Client Browser Cache CDN/Proxy Origin Server 1. Request 2. Cache Miss 3. Forward 4. Response 5. Cache & Forward 6. Cache & Display Cache-Control and ETag headers control this entire flow

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:

ETag Validation Workflow Browser Server 1. Initial Request: GET /resource 2. Response: 200 OK ETag: "abc123" Cache-Control: max-age=3600 3. Cache resource 4. Later: GET /resource If-None-Match: "abc123" 5. Response: 304 Not Modified 6. Use cached copy

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