Skip to content

Instantly share code, notes, and snippets.

@ezekielchentnik
Last active July 28, 2025 06:42
Show Gist options
  • Save ezekielchentnik/c5aa94fbec83ec1b3759fa8b3f6a00b1 to your computer and use it in GitHub Desktop.
Save ezekielchentnik/c5aa94fbec83ec1b3759fa8b3f6a00b1 to your computer and use it in GitHub Desktop.

Lucid Caching

A smart HTTP caching strategy that combines aggressive caching with guaranteed content freshness.

What is Lucid Caching?

Lucid caching is a HTTP cache control strategy that uses the following header combination for HTML files:

Cache-Control: public, max-age=31536000, no-cache

This creates a "lucid" (clear, transparent) caching behavior that gives you both aggressive performance and guaranteed freshness.

The Magic Combination:

  • public - Content can be cached by browsers and CDNs
  • max-age=31536000 - Cache for 1 year (aggressive caching)
  • no-cache - Always revalidate with server before serving

How It Works

  1. First request: Browser downloads and caches HTML with 1-year expiry
  2. Subsequent requests: Browser always asks server "Has this changed?"
  3. If unchanged: Server responds 304 Not Modified, browser uses cached version
  4. If changed: Server sends new content, browser updates cache

Why This Is Effective:

  • Near-instant loading - Browser has content cached for up to 1 year
  • Always current - Content is validated on every request
  • Minimal bandwidth - 304 responses are tiny (just headers)
  • Zero stale content - Impossible to serve outdated HTML
  • Developer friendly - No cache-busting URLs needed for HTML

Implementation Examples

Bun Static Server

import { serve, file } from "bun";

// Spin up a static file server
const server = serve({
  port: 8080,
  // SPA-aware fetch handler with smart caching
  async fetch(req) {
    try {
      const url = new URL(req.url);
      let pathname = url.pathname;

      // SPA route → rewrite to /index.html
      const isFileRequest = /\.[^/]+$/.test(pathname);
      if (!isFileRequest) {
        pathname = pathname.replace(/\/?$/, "/index.html");
      }

      // Missing file → fallback to /index.html
      if (!(await file(`./dist${pathname}`).exists())) {
        pathname = "/index.html";
      }

      // Cache-Control: immutable for hashed assets, lucid for HTML
      const headers = new Headers();
      headers.set(
        "Cache-Control",
        pathname.match(/-[a-zA-Z0-9]{8,}\./)
          ? "public, max-age=31536000, immutable"
          : "public, max-age=31536000, no-cache",
      );

      return new Response(file(`./dist${pathname}`), { headers });
    } catch (error) {
      console.error("❌ Error while serving request:", error);
      return new Response("Not Found", { status: 404 });
    }
  },
});

Nginx Configuration

server {
    listen 80;
    root /var/www/html;
    
    # Lucid caching for HTML files
    location ~* \.html$ {
        add_header Cache-Control "public, max-age=31536000, no-cache";
        try_files $uri $uri/ /index.html;
    }
    
    # Immutable caching for hashed assets
    location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        add_header Cache-Control "public, max-age=31536000, immutable";
        expires 1y;
    }
}

Comparison with Other Strategies

Strategy Header Performance Freshness Use Case
No Cache no-cache, no-store Slow ✅ Always fresh Development
Short Cache max-age=300 Medium ✅ Mostly fresh Dynamic content
Long Cache max-age=31536000 ⚡ Fast ❌ Can be stale Static assets
Immutable Cache max-age=31536000, immutable ⚡ Instant ✅ Never changes Hashed assets
Stale-While-Revalidate max-age=300, stale-while-revalidate=86400 ⚡ Instant ⚠️ Eventually fresh Long sessions
🎯 Lucid Cache max-age=31536000, no-cache ⚡ Fast ✅ Always fresh Regularly updated static sites

Combining Lucid + Immutable Caching

The perfect caching strategy combines Lucid caching for HTML with Immutable caching for static assets:

🎯 Complete Strategy:

# HTML files - Always fresh with Lucid caching
Cache-Control: public, max-age=31536000, no-cache

# Hashed assets - Forever cached with immutable
Cache-Control: public, max-age=31536000, immutable

📁 File Routing:

index.html                 → Lucid caching
about.html                 → Lucid caching
chunk-a1b2c3.js           → Immutable caching  
styles-x4y5z6.css         → Immutable caching
logo-m7n8o9.png           → Immutable caching

🔄 How They Work Together:

  1. HTML requests → Server validates freshness, serves updated content (always fresh)
  2. Asset requests → Cached forever until filename/hash changes (maximum performance)
  3. New deployments → HTML updates with new asset URLs, forcing fresh asset downloads
  4. Perfect invalidation → HTML controls when assets update (zero stale content, efficient deployments)

Lucid vs Stale-While-Revalidate

Both strategies solve caching problems but with different trade-offs:

Lucid Caching

Cache-Control: public, max-age=31536000, no-cache

Behavior:

  • Every request: Check server first, then serve from cache
  • Response time: Fast (validation + cache)
  • Freshness: Always current

Stale-While-Revalidate

Cache-Control: public, max-age=3600, stale-while-revalidate=600

Behavior:

  • First 60 minutes: Serve from cache instantly
  • Next 10 minutes: Serve stale copy, revalidate in background
  • After 70 minutes: Must fetch fresh response before serving
  • Response time: Instant (cache)
  • Freshness: May be stale, depends on timing

Trade-off Comparison

Aspect Lucid Caching Stale-While-Revalidate
Performance (fast) (instant)
Freshness Always current Potentially stale
Complexity Simple More complex timing
Predictability Consistent behavior Depends on timing
Server load Validation on every request Validation + background requests

When to Choose Which

Choose Lucid Caching when:

  • Content changes frequently (daily/weekly deploys)
  • Immediate freshness is critical
  • You want predictable behavior
  • Simplicity is important

Choose Stale-While-Revalidate when:

  • Performance is more critical than immediate freshness
  • Content changes infrequently
  • Users browse in longer sessions
  • You can tolerate temporary staleness

Why "Lucid"?

The term "lucid" means clear, transparent, and easily understood. Lucid caching creates a transparent caching behavior where:

  • Clear performance - Fast subsequent loads
  • Transparent freshness - Always current content
  • Easily understood - Simple header combination with predictable behavior

It's the clarity of getting both performance and freshness without the typical trade-offs.


Credits

Lucid Caching by Ezekiel Chentnik

Making HTTP caching both fast and fresh, one header at a time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment