- Home
- HTTP Headers
- If-Modified-Since Header
Header
If-Modified-Since Header
Learn how the If-Modified-Since header requests resources only if modified since a specific date. Reduce bandwidth with efficient conditional caching.
TL;DR: Requests resources only if modified after a specific date, enabling efficient caching. Server returns 304 Not Modified for unchanged content, saving bandwidth.
What is If-Modified-Since?
The If-Modified-Since header makes conditional requests based on modification time. It’s like asking “only send me this file if it’s newer than what I already have.” This saves bandwidth and improves performance by avoiding unnecessary downloads.
This header is essential for efficient caching and reducing server load.
How If-Modified-Since Works
1. Client requests with condition:
GET /api/posts HTTP/1.1
Host: example.com
If-Modified-Since: Wed, 17 Jan 2026 10:30:00 GMT
```text
**2a. Resource unchanged (304 response):**
```http
HTTP/1.1 304 Not Modified
Last-Modified: Wed, 17 Jan 2026 10:30:00 GMT
Cache-Control: max-age=3600
2b. Resource modified (200 response):
HTTP/1.1 200 OK
Last-Modified: Thu, 18 Jan 2026 14:20:00 GMT
Content-Type: application/json
{"posts": [...]}
```text
## Date Format
Uses HTTP-date format (RFC 7231):
```http
If-Modified-Since: Wed, 18 Jan 2026 10:30:00 GMT
If-Modified-Since: Thu, 19 Jan 2026 15:45:30 GMT
Real-World Examples
Web Page Caching
GET /index.html HTTP/1.1
Host: example.com
If-Modified-Since: Tue, 16 Jan 2026 08:00:00 GMT
```http
### API Data Caching
```http
GET /api/products HTTP/1.1
Host: shop.example.com
If-Modified-Since: Wed, 17 Jan 2026 12:00:00 GMT
Accept: application/json
Image Caching
GET /images/logo.png HTTP/1.1
Host: cdn.example.com
If-Modified-Since: Mon, 15 Jan 2026 09:30:00 GMT
```text
### RSS Feed Updates
```http
GET /feed.xml HTTP/1.1
Host: blog.example.com
If-Modified-Since: Thu, 18 Jan 2026 06:00:00 GMT
Server Response Patterns
Not Modified (304)
HTTP/1.1 304 Not Modified
Last-Modified: Wed, 17 Jan 2026 10:30:00 GMT
ETag: "abc123"
Cache-Control: max-age=3600
```text
Client should use cached version.
### Modified (200)
```http
HTTP/1.1 200 OK
Last-Modified: Thu, 18 Jan 2026 14:20:00 GMT
Content-Type: text/html
Content-Length: 1247
<html>...</html>
Resource has changed, here’s the new content.
Precondition Failed (412)
HTTP/1.1 412 Precondition Failed
Content-Type: application/json
{"error": "Invalid If-Modified-Since date"}
```javascript
## Client Implementation
### JavaScript/Fetch
```javascript
// Store last modified date
let lastModified = localStorage.getItem('posts-last-modified')
const headers = {}
if (lastModified) {
headers['If-Modified-Since'] = lastModified
}
const response = await fetch('/api/posts', { headers })
if (response.status === 304) {
// Use cached data
const cachedData = JSON.parse(localStorage.getItem('posts-data'))
return cachedData
} else if (response.ok) {
// Update cache with new data
const newData = await response.json()
const newLastModified = response.headers.get('Last-Modified')
localStorage.setItem('posts-data', JSON.stringify(newData))
localStorage.setItem('posts-last-modified', newLastModified)
return newData
}
Browser Automatic Behavior
// Browsers automatically add If-Modified-Since for cached resources
// when you refresh a page or revisit it
```javascript
## Server Implementation
### Express.js Example
```javascript
app.get('/api/posts', (req, res) => {
const posts = getPosts()
const lastModified = getPostsLastModified()
// Check If-Modified-Since header
const ifModifiedSince = req.headers['if-modified-since']
if (ifModifiedSince) {
const clientDate = new Date(ifModifiedSince)
if (lastModified <= clientDate) {
// Not modified
return res.status(304).set('Last-Modified', lastModified.toUTCString()).end()
}
}
// Modified or first request
res.set('Last-Modified', lastModified.toUTCString())
res.json(posts)
})
Static File Serving
// Most web servers handle this automatically
app.use(
express.static('public', {
lastModified: true, // Enable Last-Modified headers
etag: true // Also enable ETag for better caching
})
)
```text
## Best Practices
### Always Set Last-Modified
```javascript
// ✅ Always include Last-Modified in responses
res.set('Last-Modified', resource.updatedAt.toUTCString())
res.json(resource)
Handle Invalid Dates
app.get('/resource', (req, res) => {
const ifModifiedSince = req.headers['if-modified-since']
if (ifModifiedSince) {
const clientDate = new Date(ifModifiedSince)
// Check for invalid date
if (isNaN(clientDate.getTime())) {
return res.status(400).json({ error: 'Invalid If-Modified-Since date' })
}
// Continue with comparison...
}
})
```javascript
### Combine with ETag
```javascript
app.get('/data', (req, res) => {
const data = getData()
const lastModified = data.updatedAt
const etag = generateETag(data)
// Check both conditions
const ifModifiedSince = req.headers['if-modified-since']
const ifNoneMatch = req.headers['if-none-match']
let notModified = false
if (ifModifiedSince) {
notModified = lastModified <= new Date(ifModifiedSince)
}
if (ifNoneMatch) {
notModified = notModified && ifNoneMatch === etag
}
if (notModified) {
return res.status(304).set('Last-Modified', lastModified.toUTCString()).set('ETag', etag).end()
}
res.set('Last-Modified', lastModified.toUTCString())
res.set('ETag', etag)
res.json(data)
})
Performance Benefits
Bandwidth Savings
Without If-Modified-Since:
- Request: 500 bytes
- Response: 50KB (full content)
- Total: ~50KB
With If-Modified-Since (304):
- Request: 600 bytes (with header)
- Response: 200 bytes (304 status)
- Total: ~800 bytes (99% savings!)
Reduced Server Load
- Skip database queries for unchanged data
- Avoid JSON serialization
- Reduce CPU and memory usage
Testing
Using curl
# First request (gets Last-Modified)
curl -v https://example.com/api/posts
# Conditional request
curl -H "If-Modified-Since: Wed, 18 Jan 2026 10:30:00 GMT" \
https://example.com/api/posts
# Should return 304 if not modified
Browser DevTools
- Open Network tab
- Load page normally
- Refresh page (Ctrl+R)
- Look for 304 responses with If-Modified-Since headers
Related Headers
- Last-Modified - Server’s modification timestamp
- If-None-Match - ETag-based conditional requests
- Cache-Control - Caching directives
- ETag - Resource version identifier
Timezone Gotchas and Clock Skew
The If-Modified-Since header uses HTTP-date format, which is always in GMT. A common mistake is generating the date in local time or using a format that includes a timezone offset. Servers must parse the date correctly and compare it against the resource’s last-modified time in UTC.
Clock skew between client and server can cause unexpected behavior. If the client’s clock is ahead of the server’s clock, the client might send an If-Modified-Since date that is in the server’s future, causing the server to always return 304 even for recently modified resources. This is one reason why If-None-Match with ETags is generally more reliable than If-Modified-Since for cache validation — ETags are based on content identity rather than time.
The If-Modified-Since header is also less precise than ETags for resources that change multiple times within a single second. If a resource is modified twice in the same second, the second modification has the same Last-Modified timestamp as the first, so a client that cached the first version would incorrectly receive a 304 for the second version. ETags avoid this problem because they are based on content hash rather than modification time.
Frequently Asked Questions
What is If-Modified-Since?
If-Modified-Since is a conditional request header. The client sends the Last-Modified date from a cached response, and the server returns 304 if unchanged or 200 with new content.
How does If-Modified-Since improve performance?
It avoids re-downloading unchanged resources. The server only sends the full response if the resource changed after the specified date, saving bandwidth.
What is the difference between If-Modified-Since and If-None-Match?
If-Modified-Since uses timestamps with Last-Modified. If-None-Match uses ETags for more precise validation. If-None-Match takes precedence when both are present.
What format does If-Modified-Since use?
It uses HTTP-date format in GMT: "If-Modified-Since: Wed, 21 Oct 2026 07:28:00 GMT". The date comes from the Last-Modified header of the cached response.