304 🌐 HTTP

HTTP 304 Not Modified

The cached copy is still valid, so the server sends no body and the client reuses its cache.

Seen on: Nginx Apache

Meaning

304 is a performance win, not an error. It answers conditional requests (If-None-Match with an ETag, or If-Modified-Since). It becomes a problem when users keep seeing stale CSS/JS/API data, or when code treats 304 as a failure.

Common causes

  • Static assets changed but filenames/ETags didn’t (no cache busting)
  • ETags differ across servers behind a load balancer, causing needless 200s
  • API clients mishandling empty 304 bodies
  • Proxy/CDN caching with long max-age

⚡ Quick fix

  1. Version static files (app.css?v=123 or hashed filenames)
  2. Purge the CDN cache after deploys
  3. In fetch, use cache: 'no-store' for data that must be fresh
  4. Treat 304 as success when you send conditional headers yourself

Detailed fix by platform

Nginx

  1. Long cache for hashed assets, revalidate HTML:
    nginx
    location ~* \.(css|js|png|svg|woff2)$ { expires 30d; add_header Cache-Control "public, immutable"; }
    location / { add_header Cache-Control "no-cache"; }

Apache

  1. Use mod_expires/mod_headers; with multiple servers, FileETag MTime Size avoids inode-based ETag mismatches.

Code examples

See the conditional request

bash
curl -sI https://example.com/app.css | grep -i etag
curl -sI -H 'If-None-Match: "65f1-5e2a"' https://example.com/app.css   # → HTTP/2 304

How to diagnose

  1. Freshness — Is the user actually seeing stale content?
  2. Validators — ETag / Last-Modified values before and after deploy
  3. Caching layers — Browser, CDN, proxy — which one served it?

🧠 Still stuck? Analyze your error

Paste the full message, response headers or stack trace — we'll detect the platform and point to the most likely cause.