Cache-Control on a personalized response sets the storage boundary; a valid ETag alone does not make a tenant receipt safe for a shared cache.
Spring MVC private Cache-Control: decide who may retain a receipt
Bind the cache decision to the data
A receipt total is visible only to the authenticated tenant. A browser may keep a short-lived copy if product policy permits it, but an intermediary must not reuse the response for another user. Set a private directive on the response, and decide separately whether sensitive fields require no-store. Conditional GET] saves response bytes after authorization; it is not an authorization check.
Validate before a conditional response
A client may send If-None-Match for a receipt it no longer owns. Resolve the tenant and authorize that receipt before computing the entity tag or returning 304. Scope the tag to the representation and tenant; changing visibility or serialization must change the validator. A private cache can still retain data on a shared device, so use no-store for a receipt that must leave no local copy.
Test through the full path
Request the same receipt as its owner and as a second tenant, including a matching If-None-Match header. The owner may receive 304 after a prior 200; the second tenant must never get a revealing 304. Inspect Cache-Control on both 200 and 304. If a reverse proxy rewrites headers, test that deployed edge as well.
Implementation contract
@GetMapping("/receipts/{receiptId}")
ResponseEntity<ReceiptView> readReceipt(
@PathVariable UUID receiptId, Authentication authentication) {
ReceiptView receipt = receiptService.authorizedView(authentication, receiptId);
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(Duration.ofMinutes(2)).cachePrivate())
.eTag("\"" + receipt.representationVersion() + "\"")
.body(receipt);
}Cost and verification
A short private cache can reduce repeated transfer and rendering. ETag calculation still costs enough to load the authorized version; no-store trades this saving for tighter data retention.
Common Mistakes
- Do not mark tenant-specific receipts public.
- Do not emit a 304 before checking current authorization.
- Do not use a stable ETag when the visible representation or permissions changed.
Read next
Spring MVC conditional GET: validate the representation tag, Spring MVC consumes and produces: 415 and 406 are different failures, Spring Security filter chain: authentication, CSRF and request order, Spring MVC byte ranges: resume an authorized receipt download, Spring MVC Content-Disposition: produce a safe attachment filename.
