← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

HTTP Caching (ETags, Cache-Control)

Performance Intermediate
debt(d5/e3/b5/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The detection_hints.tools list includes lighthouse, curl, and webpagetest — all specialist tools that must be deliberately run to surface missing or misconfigured Cache-Control headers. Misconfigured caching is not caught by compilers or default linters; it requires running a performance audit or inspecting HTTP response headers manually.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix describes a pattern-replacement approach: assign the correct Cache-Control directive based on asset type (hashed static → immutable, authenticated pages → no-store, dynamic public content → ETag). This is more than a single-line patch because it requires categorising different response types and applying the right header to each, but it stays within one configuration layer (server config, middleware, or CDN rules) without touching application logic broadly.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). HTTP caching applies across all web and API contexts (applies_to: web, api). Every future feature that serves a new resource type forces a decision about its caching policy. Wrong choices (e.g., no-cache on static assets, or caching user-specific data) create ongoing performance or correctness problems across many work streams. It's not as gravity-defining as an ORM choice (b7+), but it imposes a recurring correctness tax on all response-serving code.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field explicitly states the canonical wrong belief: developers assume Cache-Control: no-cache means 'never cache,' when it actually means 'revalidate before use.' The directive that truly prevents caching is no-store. This contradicts the plain-English reading of the directive name and contradicts how developers familiar with other caching systems (e.g., application-level caches) expect 'no-cache' to behave, making it a serious semantic trap.

About DEBT scoring →

Also Known As

HTTP cache Cache-Control ETag Last-Modified

TL;DR

Standard HTTP headers that instruct browsers and intermediary caches on how long to cache responses and when to revalidate.

Explanation

HTTP caching is controlled primarily by Cache-Control (max-age, no-cache, no-store, public/private), ETags (opaque hash of response content enabling conditional requests), and Last-Modified. Correct caching headers can eliminate round-trips entirely for static assets and reduce server load for API responses. In PHP, set cache headers explicitly with header() — framework responses typically provide helper methods. For dynamic content, use Vary headers to cache different representations correctly and implement conditional GET responses that return 304 Not Modified without a body.

Common Misconception

Setting Cache-Control: no-cache means the response is never cached. no-cache means the browser must revalidate with the server before using the cached copy — the response is still stored locally. no-store is the directive that prevents caching entirely.

Why It Matters

HTTP caching headers let browsers and CDNs serve content without hitting your server — correct cache directives can eliminate 80%+ of requests for static and semi-static content.

Common Mistakes

  • No Cache-Control header — browsers guess, often defaulting to no caching.
  • Cache-Control: no-cache on truly static assets — forces revalidation on every request.
  • Not using ETags or Last-Modified for dynamic content that rarely changes.
  • Caching responses that contain user-specific data without Vary: Cookie or private directive.

Code Examples

✗ Vulnerable
// No cache headers — browser re-fetches on every navigation:
header('Content-Type: application/json');
echo json_encode($data);
// Should add: header('Cache-Control: public, max-age=300');
// Or for user data: header('Cache-Control: private, max-age=60');
✓ Fixed
// ETags — conditional GET, saves bandwidth
public function show(int $id): Response {
    $user = $this->users->find($id);
    $etag = md5($user->updatedAt->format('U'));

    if ($this->request->header('If-None-Match') === $etag) {
        return response('', 304); // Not Modified — no body sent
    }

    return response()->json($user)
        ->header('ETag', $etag)
        ->header('Cache-Control', 'private, must-revalidate');
}

// Static assets — immutable long cache
header('Cache-Control: public, max-age=31536000, immutable');

// API responses — short cache, must revalidate
header('Cache-Control: private, max-age=60, must-revalidate');

Added 15 Mar 2026
Edited 22 Mar 2026
Views 76
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 4 pings F 3 pings S 9 pings S 2 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Scrapy 16 Amazonbot 10 Perplexity 10 Ahrefs 8 Google 5 SEMrush 5 Unknown AI 2 Claude 2 Bing 2 Majestic 1 Meta AI 1
crawler 56 crawler_json 6
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Medium
⚡ Quick Fix
Set Cache-Control: public, max-age=31536000, immutable on hashed static assets; Cache-Control: no-store on authenticated pages; ETag or Last-Modified on public but changeable content
📦 Applies To
any web api
🔗 Prerequisites
🔍 Detection Hints
Static assets without Cache-Control header; no-cache on all responses including static files; missing ETag on cacheable resources
Auto-detectable: ✓ Yes lighthouse curl webpagetest
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Medium Context: File


✓ schema.org compliant