HTTP

304 - Not Modified

Getting a 304 Not Modified means the resource hasn't changed since your last request—the ETag or Last-Modified timestamp matches, so you can use your cached version. This client-side informational response (3xx) saves bandwidth by not sending the full response. Most common when conditional requests use If-None-Match or If-Modified-Since, but also appears when cached resources are still valid, ETags match, or Last-Modified timestamps are unchanged.

#Common Causes

  • Frontend: Resource cached and still valid. ETag matches If-None-Match. Last-Modified unchanged. Conditional request sent.
  • Backend: Resource not modified since last request. ETag validation passed. Last-Modified check passed. Cache validation successful.
  • Infrastructure: CDN serves cached content. Reverse proxy cache valid. Load balancer cache hit.

Solutions

  1. 1Step 1: Diagnose - Check DevTools Network tab—304 responses mean cache is valid. Review ETag headers. Check Last-Modified headers. Verify cache is working.
  2. 2Step 2: Diagnose - Server logs show cache validation. Review ETag generation. Check Last-Modified timestamps. Verify conditional request handling.
  3. 3Step 3: Fix - Client-side: Use cached version when 304 received. Implement ETag/Last-Modified caching. Handle conditional requests properly. Update cache on 200.
  4. 4Step 4: Fix - Server-side: Return 304 with proper headers. Generate ETags correctly. Set Last-Modified timestamps. Implement conditional request handling.
  5. 5Step 5: Fix - Infrastructure: Configure CDN cache headers. Set reverse proxy cache. Update load balancer cache settings.

</>Code Examples

Fetch API: Handle 304 Not Modified
1// Client-side: Handle 304 and use cache
2async function fetchWithCache(url) {
3  const cached = localStorage.getItem(`cache-${url}`);
4  const cacheData = cached ? JSON.parse(cached) : null;
5  
6  // Build conditional request headers
7  const headers = {};
8  if (cacheData?.etag) {
9    headers['If-None-Match'] = cacheData.etag;
10  }
11  if (cacheData?.lastModified) {
12    headers['If-Modified-Since'] = cacheData.lastModified;
13  }
14  
15  const response = await fetch(url, { headers });
16  
17  if (response.status === 304) {
18    // Resource not modified - use cached version
19    console.log('Using cached version (304 Not Modified)');
20    return cacheData.data;
21  }
22  
23  // Resource modified - update cache
24  const data = await response.json();
25  const etag = response.headers.get('ETag');
26  const lastModified = response.headers.get('Last-Modified');
27  
28  // Update cache
29  localStorage.setItem(`cache-${url}`, JSON.stringify({
30    data,
31    etag,
32    lastModified,
33    timestamp: Date.now(),
34  }));
35  
36  return data;
37}
38
39// ETag-based caching
40async function fetchWithETag(url) {
41  const cached = localStorage.getItem(`etag-${url}`);
42  const cacheData = cached ? JSON.parse(cached) : null;
43  
44  const headers = {};
45  if (cacheData?.etag) {
46    headers['If-None-Match'] = cacheData.etag;
47  }
48  
49  const response = await fetch(url, { headers });
50  
51  if (response.status === 304) {
52    return cacheData.data;
53  }
54  
55  const data = await response.json();
56  const etag = response.headers.get('ETag');
57  
58  localStorage.setItem(`etag-${url}`, JSON.stringify({
59    data,
60    etag,
61    timestamp: Date.now(),
62  }));
63  
64  return data;
65}
66
67// Last-Modified caching
68async function fetchWithLastModified(url) {
69  const cached = localStorage.getItem(`modified-${url}`);
70  const cacheData = cached ? JSON.parse(cached) : null;
71  
72  const headers = {};
73  if (cacheData?.lastModified) {
74    headers['If-Modified-Since'] = cacheData.lastModified;
75  }
76  
77  const response = await fetch(url, { headers });
78  
79  if (response.status === 304) {
80    return cacheData.data;
81  }
82  
83  const data = await response.json();
84  const lastModified = response.headers.get('Last-Modified');
85  
86  localStorage.setItem(`modified-${url}`, JSON.stringify({
87    data,
88    lastModified,
89    timestamp: Date.now(),
90  }));
91  
92  return data;
93}
Express.js: Return 304 for Unchanged Resources
1// Server-side: Return 304 for conditional requests
2const express = require('express');
3const app = express();
4
5// ETag-based conditional request
6app.get('/api/resource/:id', async (req, res) => {
7  const resource = await db.resources.findById(req.params.id);
8  
9  // Generate ETag from resource
10  const etag = generateETag(resource);
11  res.set('ETag', etag);
12  
13  // Check If-None-Match header
14  const ifNoneMatch = req.headers['if-none-match'];
15  if (ifNoneMatch === etag) {
16    // Resource not modified
17    return res.status(304).end();
18  }
19  
20  // Resource modified - return full response
21  res.json(resource);
22});
23
24// Last-Modified conditional request
25app.get('/api/data/:id', async (req, res) => {
26  const data = await db.data.findById(req.params.id);
27  const lastModified = new Date(data.updatedAt).toUTCString();
28  
29  res.set('Last-Modified', lastModified);
30  
31  // Check If-Modified-Since header
32  const ifModifiedSince = req.headers['if-modified-since'];
33  if (ifModifiedSince) {
34    const modifiedSince = new Date(ifModifiedSince);
35    const resourceModified = new Date(data.updatedAt);
36    
37    if (resourceModified <= modifiedSince) {
38      // Resource not modified
39      return res.status(304).end();
40    }
41  }
42  
43  // Resource modified - return full response
44  res.json(data);
45});
46
47// Combined ETag and Last-Modified
48app.get('/api/content/:id', async (req, res) => {
49  const content = await db.content.findById(req.params.id);
50  const etag = generateETag(content);
51  const lastModified = new Date(content.updatedAt).toUTCString();
52  
53  res.set('ETag', etag);
54  res.set('Last-Modified', lastModified);
55  
56  // Check both conditions
57  const ifNoneMatch = req.headers['if-none-match'];
58  const ifModifiedSince = req.headers['if-modified-since'];
59  
60  if (ifNoneMatch === etag) {
61    return res.status(304).end();
62  }
63  
64  if (ifModifiedSince) {
65    const modifiedSince = new Date(ifModifiedSince);
66    const resourceModified = new Date(content.updatedAt);
67    if (resourceModified <= modifiedSince) {
68      return res.status(304).end();
69    }
70  }
71  
72  res.json(content);
73});
74
75function generateETag(data) {
76  // Simple ETag generation (use crypto for production)
77  const hash = require('crypto')
78    .createHash('md5')
79    .update(JSON.stringify(data))
80    .digest('hex');
81  return `"${hash}"`;
82}
Nginx: Cache Validation Headers
1# Nginx: Configure cache validation for 304 responses
2server {
3    listen 80;
4    server_name api.example.com;
5    
6    location /api/static/ {
7        # Enable ETag
8        etag on;
9        
10        # Enable Last-Modified
11        if_modified_since exact;
12        
13        # Proxy to backend
14        proxy_pass http://backend;
15        proxy_set_header Host $host;
16        
17        # Pass conditional headers
18        proxy_set_header If-None-Match $http_if_none_match;
19        proxy_set_header If-Modified-Since $http_if_modified_since;
20        
21        # Backend returns 304 if not modified
22    }
23    
24    # Static file serving with 304
25    location /static/ {
26        root /var/www;
27        etag on;
28        if_modified_since exact;
29        
30        # Nginx automatically handles 304 for static files
31    }
32    
33    # Cache validation
34    location /api/cached/ {
35        proxy_pass http://backend;
36        proxy_cache_valid 200 1h;
37        proxy_cache_valid 304 1h;
38        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
39        
40        # Pass conditional headers
41        proxy_set_header If-None-Match $http_if_none_match;
42        proxy_set_header If-Modified-Since $http_if_modified_since;
43    }
44}

Related Errors

Provider Information

This error code is specific to HTTP services. For more information, refer to the official HTTP documentation.

304 - Not Modified | HTTP Error Reference | Error Code Reference