304 Status Code Explained
The 304 status code tells browsers that the page they requested has not changed since the last visit. It keeps sites fast and servers calm.
Understanding this single response can reduce bandwidth, speed up repeat visits, and lower hosting costs for developers and businesses alike.
What the 304 Status Code Actually Means
Core Definition in Plain Language
A 304 response is a polite “nothing new to see here.”
It appears when the server decides the browser already owns the freshest copy of a resource.
Instead of sending the file again, the server sends only the status line and headers.
How It Differs from 200 and 404
A 200 delivers the full resource every time.
A 404 signals the resource cannot be found.
304 sits between them: the resource exists, but the browser already has it cached.
Triggers That Cause a 304 Response
Request Headers That Start the Process
The browser adds If-Modified-Since or If-None-Match headers to its request.
These headers carry either a timestamp or an entity tag from a previous visit.
The server checks them against the file’s current version to decide what to send back.
Server-Side Checks
The server compares the browser’s timestamp with the file’s last-modified value.
If they match, the server returns 304.
This simple comparison saves kilobytes without touching application logic.
Browser Caching and 304 Handshakes
Cache-Control vs. 304 Logic
Cache-Control headers can make a browser skip the request entirely.
When max-age expires, the browser must ask the server again.
The 304 response is the server’s way of saying “keep using what you have.”
ETag as a Fingerprint
An ETag is a unique string generated from file content.
Even if the last-modified date is spoofed, mismatched ETags force a fresh 200 response.
This tightens validation and prevents stale data from slipping through.
Practical Impact on Web Performance
Faster Repeat Page Loads
Visitors who return see pages pop open almost instantly.
304 removes the download phase, cutting perceived wait time to near zero.
Search engines notice this speed and may rank the site more favorably.
Reduced Bandwidth Bills
Every 304 reply saves the bytes of an entire file.
For high-traffic assets like images or scripts, the savings scale quickly.
Hosting providers often bill by data transfer, so 304 keeps invoices smaller.
Debugging 304 Issues in DevTools
Spotting 304 in Network Panels
Open DevTools, reload the page, and watch the status column.
Rows labeled 304 did not transfer the body.
Hover over the size column; you will see “cached” or a tiny header-only payload.
Forcing a Fresh 200 Response
Hold Shift while clicking refresh to override the cache.
The browser drops the conditional headers and requests the full file again.
Use this trick to test changes without clearing the entire cache.
Configuring 304 Support on Common Servers
Apache via mod_headers and mod_expires
Add ExpiresByType directives to set last-modified headers automatically.
Enable ETag generation with FileETag MTime Size.
Restart Apache; conditional requests now trigger 304 when files remain unchanged.
Nginx with Headers and ETag Modules
Load the ngx_http_headers_module to control Cache-Control.
Add etag on; inside each location block that serves static assets.
Reload Nginx; unchanged files will start returning 304 on subsequent visits.
Node.js Express Setup
Install the etag package and wire it into your static middleware.
Use app.use(express.static(‘public’, { etag: true, lastModified: true })).
Restart the server; browser reloads will now negotiate 304 where appropriate.
Edge Cases and Misconfigurations
Clock Skew Between Server and Client
If the server’s clock is ahead, the browser may believe its copy is stale.
This causes unnecessary 200 responses and wastes bandwidth.
Synchronize server time through NTP to keep timestamps trustworthy.
Overly Aggressive Cache Headers
Setting max-age to years can hide legitimate updates from users.
Combine long max-age with fingerprinted filenames to break the cache when needed.
Static assets like style.3e8a2.css will always be new, so 304 never fires for them.
Testing 304 Behavior End-to-End
Using curl for Quick Checks
Run curl -I -H “If-Modified-Since: $(date -u -d ‘1 day ago’ +’%a, %d %b %Y %H:%M:%S GMT’)” https://example.com/logo.png.
A 304 response confirms your server is honoring the conditional header.
Switch the date to yesterday minus one second to see the file come back as 200.
Automated Regression Tests
Write a small script that fetches a resource twice using the same headers.
Assert that the second request returns 304 and Content-Length is absent.
Add this test to your CI pipeline to prevent accidental misconfigurations.
Security Considerations Around 304
Information Leakage via ETags
ETags that reveal inode numbers can expose server internals.
Use a hash of file contents instead of filesystem metadata.
This keeps the validation token opaque and safe.
Cache Poisoning Vectors
A misbehaving proxy might strip conditional headers and cache an outdated 200 response.
Set Cache-Control: no-cache on authenticated endpoints to stop intermediaries from storing sensitive pages.
Combine this with proper Vary headers to guide caches correctly.
SEO and User Experience Benefits
Core Web Vitals Connection
Largest Contentful Paint improves when heavy hero images load from cache via 304.
Google’s bots also issue conditional requests, so 304 answers save crawl budget.
More crawl budget means deeper indexing of your site’s pages.
Bounce Rate Reduction
Returning visitors abandon slow sites quickly.
Instant 304-backed page loads keep them engaged and scrolling.
This subtle speed boost can lift conversions without extra marketing spend.
When to Avoid 304 and Use 200 Instead
Dynamic Content That Changes Frequently
Dashboards with live stats should not rely on 304.
Cache-Control: no-store ensures every request fetches fresh data.
Skip ETags entirely for these endpoints to prevent accidental reuse.
Personalized Pages
A user profile page varies per visitor.
Conditional headers would compare the wrong entity, leading to 304 when the page is actually different for another user.
Set Cache-Control: private, no-cache to force fresh rendering each time.
Future-Proofing Your 304 Strategy
Adopting Service Workers
A service worker can intercept requests and decide locally whether to hit the network.
Pair it with Cache API storage to simulate 304 logic offline.
Update the worker script when files change so users receive new assets seamlessly.
HTTP/2 and 304 Interaction
Multiplexing makes multiple 304 responses cheap on a single connection.
Header compression shrinks conditional headers to a few bytes.
This synergy means 304 remains useful even under modern protocols.