HTTP 304 Not Modified — Browser Caching & ETags Explained
The cached version of the resource is still current — the client should use its cached copy instead of downloading again. Sent when the client includes If-None-Match (ETag) or If-Modified-Since headers and the resource hasn't changed.
When to Return 304
Implement ETag or Last-Modified headers on responses. When the client sends a conditional request (If-None-Match, If-Modified-Since) and the resource hasn't changed, respond with 304 and no body. The browser uses its cache.
Common Causes
- Client re-validating a cached resource
- CDN or proxy checking if content needs refreshing
HTTP Response Example
HTTP/1.1 304 Not Modified
ETag: "33a64df5"
Cache-Control: max-age=3600Code Examples
Express.js
app.get('/api/data', async (req, res) => {
const data = await db.getData()
const etag = computeETag(data)
if (req.headers['if-none-match'] === etag) {
return res.sendStatus(304) // No body
}
res
.set('ETag', etag)
.set('Cache-Control', 'max-age=60, must-revalidate')
.json(data)
})Next.js App Router
// Next.js static assets and pages handle ETags automatically.
// For API routes with caching:
export async function GET(request: Request) {
const data = await getData()
const etag = computeETag(data)
if (request.headers.get('if-none-match') === etag) {
return new Response(null, { status: 304 })
}
return Response.json(data, {
headers: { ETag: etag, 'Cache-Control': 'max-age=60' },
})
}Related Status Codes
Frequently Asked Questions
What does HTTP 304 Not Modified mean?
The cached version of the resource is still current — the client should use its cached copy instead of downloading again. Sent when the client includes If-None-Match (ETag) or If-Modified-Since headers and the resource hasn't changed.
When should an API return 304?
Implement ETag or Last-Modified headers on responses. When the client sends a conditional request (If-None-Match, If-Modified-Since) and the resource hasn't changed, respond with 304 and no body. The browser uses its cache.
What causes an HTTP 304 error?
Common causes: Client re-validating a cached resource; CDN or proxy checking if content needs refreshing.
