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!