HTTP Topic

HTTP Caching Mechanisms

What You'll Learn:

  • Browser caching vs. intermediary caching

  • Cache control directives and their usage

  • ETags and conditional requests

Detailed Explanation:

Caching Layers

1. Browser Cache

  • Private cache for single user

  • Controlled by Cache-Control: private

2. Intermediary Cache

  • Shared cache (CDN, proxy servers)

  • Controlled by Cache-Control: public

3. Gateway Cache

  • Reverse proxies, CDN edge servers

Cache Control Directives

Basic Cache Control:

# Cache for 1 hour
Cache-Control: max-age=3600

# Don't cache at all
Cache-Control: no-store

# Cache but validate with server
Cache-Control: no-cache

# Public caching allowed
Cache-Control: public, max-age=3600

# Private caching only
Cache-Control: private, max-age=3600

Advanced Cache Control:

# Complex caching strategy
Cache-Control: public, max-age=3600, s-maxage=7200, must-revalidate

# Don't transform content (e.g., compression)
Cache-Control: no-transform

# Stale while revalidate
Cache-Control: max-age=3600, stale-while-revalidate=300

Validation-Based Caching

ETag (Entity Tag):

# First request
GET /api/users/123 HTTP/1.1

# Response with ETag
HTTP/1.1 200 OK
ETag: "v1-abc123"
Cache-Control: max-age=300
Content-Type: application/json

{"id": 123, "name": "John Doe"}

# Subsequent request with ETag
GET /api/users/123 HTTP/1.1
If-None-Match: "v1-abc123"

# Server response if not modified
HTTP/1.1 304 Not Modified
ETag: "v1-abc123"

Last-Modified:

# First request
GET /api/users/123 HTTP/1.1

# Response with Last-Modified
HTTP/1.1 200 OK
Last-Modified: Mon, 23 Jan 2023 10:00:00 GMT
Cache-Control: max-age=300

# Subsequent request
GET /api/users/123 HTTP/1.1
If-Modified-Since: Mon, 23 Jan 2023 10:00:00 GMT

# Server response if not modified
HTTP/1.1 304 Not Modified

Cache Strategies for Different Content Types

Static Assets (CSS, JS, Images):

Cache-Control: public, max-age=31536000, immutable

API Responses (Dynamic Data):

Cache-Control: private, no-cache
# or
Cache-Control: private, max-age=60, must-revalidate

User-Specific Content:

Cache-Control: private, no-store
# or
Cache-Control: private, max-age=0, must-revalidate

Spring Boot Caching Implementation

Controller-Level Caching:

@RestController
public class UserController {
    
    @GetMapping("/api/users/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        
        return ResponseEntity.ok()
            .cacheControl(CacheControl.maxAge(30, TimeUnit.MINUTES))
            .eTag(user.getVersion().toString())
            .body(user);
    }
    
    @GetMapping("/api/static/config")
    public ResponseEntity<Config> getConfig() {
        Config config = configService.getConfig();
        
        return ResponseEntity.ok()
            .cacheControl(CacheControl.noCache())
            .body(config);
    }
}

ETag Filter for Dynamic Content:

@Component
public class EtagFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // Create wrapper to capture response content
        ContentCachingResponseWrapper responseWrapper = 
            new ContentCachingResponseWrapper(httpResponse);
        
        chain.doFilter(request, responseWrapper);
        
        // Generate ETag from response content
        byte[] content = responseWrapper.getContentAsByteArray();
        String etag = generateETag(content);
        
        // Check If-None-Match header
        String ifNoneMatch = httpRequest.getHeader("If-None-Match");
        if (etag.equals(ifNoneMatch)) {
            httpResponse.setStatus(HttpStatus.NOT_MODIFIED.value());
        } else {
            httpResponse.setHeader("ETag", etag);
            responseWrapper.copyBodyToResponse();
        }
    }
    
    private String generateETag(byte[] content) {
        // Simple ETag generation
        return "\"" + Integer.toHexString(Arrays.hashCode(content)) + "\"";
    }
}

Cache Invalidation Strategies

1. Time-Based Expiration:

Cache-Control: max-age=3600

2. Version-Based URLs:

GET /static/js/app-v1.2.3.js
GET /static/css/styles-20230123.css

3. Cache Busting with Query Parameters:

GET /api/users?v=202301231030

4. Manual Invalidation:

  • Purge CDN cache via API

  • Update resource to change ETag

Practical Caching Examples

API Response with Smart Caching:

@GetMapping("/api/products/{id}")
public ResponseEntity<Product> getProduct(@PathVariable String id) {
    Product product = productService.findById(id);
    
    CacheControl cacheControl;
    if (product.isStatic()) {
        cacheControl = CacheControl.maxAge(7, TimeUnit.DAYS).cachePublic();
    } else if (product.isFrequentlyUpdated()) {
        cacheControl = CacheControl.maxAge(1, TimeUnit.MINUTES).cachePrivate();
    } else {
        cacheControl = CacheControl.maxAge(1, TimeUnit.HOURS).cachePrivate();
    }
    
    return ResponseEntity.ok()
        .cacheControl(cacheControl)
        .eTag(product.getVersion())
        .body(product);
}

 

Comments (0)

No comments yet. Be the first to comment!

LecturreAI Assistant

Hello! I'm LecturreAI

What would you like to learn today?