info http
HTTP 304 Not Modified
Understanding HTTP 304 Not Modified - the resource has not been modified since the last request, so the cached version can be used.
What It Means
HTTP 304 Not Modified indicates that the requested resource has not been modified since the version specified by the If-Modified-Since or If-None-Match request headers. The server does not return a body — the client should use its cached copy of the resource.
This is a critical part of HTTP caching and helps reduce bandwidth and improve page load performance.
Common Causes
- Browser sends a conditional request with
If-None-Match(ETag) and the resource hasn’t changed - Browser sends
If-Modified-Sinceand the resource hasn’t been modified since that date - CDN caching layers validating that cached content is still fresh
- API clients using conditional requests to poll for changes efficiently
- Static assets (CSS, JS, images) that haven’t changed between requests
How to Fix
HTTP 304 is a normal caching response. Here’s how to implement it properly:
Express.js with ETag
const express = require('express');
const app = express();
// Express sends ETags and handles 304 automatically for static files
app.use(express.static('public'));
// For dynamic content, set ETag manually
app.get('/api/data', (req, res) => {
const data = getData();
const etag = generateETag(data);
// Check if client has current version
if (req.headers['if-none-match'] === etag) {
return res.status(304).end();
}
res.set('ETag', etag);
res.json(data);
});
Nginx caching headers
# Enable ETag (on by default)
etag on;
# Set Cache-Control for static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
Client-side conditional requests
// Using fetch with conditional headers
let cachedETag = null;
let cachedData = null;
async function fetchData() {
const headers = {};
if (cachedETag) {
headers['If-None-Match'] = cachedETag;
}
const response = await fetch('/api/data', { headers });
if (response.status === 304) {
// Use cached data
return cachedData;
}
cachedETag = response.headers.get('ETag');
cachedData = await response.json();
return cachedData;
}
Python requests
import requests
# First request
response = requests.get('https://api.example.com/data')
etag = response.headers.get('ETag')
# Subsequent requests with conditional header
response = requests.get(
'https://api.example.com/data',
headers={'If-None-Match': etag}
)
if response.status_code == 304:
print('Resource not modified, use cached version')