# Date Header

> Learn how the Date header specifies when the HTTP message was originated by the server. Understand date formats and its role in caching and logging.

Source: https://howhttpworks.com/headers/date
Last reviewed: 2026-10-04

> **TL;DR:** Timestamp showing when the HTTP response was generated by the server. Required in all responses and used for cache freshness calculations and debugging.

## What is Date?

The **Date** header contains the date and time at which the HTTP message was originated. It's like a timestamp saying "This response was created at exactly this moment."

This header is required in all HTTP responses and is used for caching calculations, age determination, and synchronization purposes.

## How Date Works

**Server sends response with Date:**

```http
HTTP/1.1 200 OK
Date: Sat, 18 Jan 2026 10:30:00 GMT
Content-Type: application/json
Cache-Control: max-age=3600

{"message": "Hello World"}
```

The Date header tells the client when the response was generated, which helps with cache freshness calculations.

## Syntax

```http
Date: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
```

### Format

The Date header uses HTTP-date format (IMF-fixdate):

- Day name: Mon, Tue, Wed, Thu, Fri, Sat, Sun
- Month: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
- Always in GMT/UTC timezone

## Common Examples

### Standard Response

```http
Date: Sat, 18 Jan 2026 10:30:00 GMT
```

Response generated on January 18, 2026 at 10:30 AM GMT.

### With Caching

```http
Date: Sat, 18 Jan 2026 10:30:00 GMT
Cache-Control: max-age=3600
Age: 300
```

Response is 5 minutes old (Age), expires 1 hour after Date.

### API Response

```http
Date: Sat, 18 Jan 2026 14:25:30 GMT
Content-Type: application/json
X-RateLimit-Reset: 1737207930
```

Date header helps correlate with rate limit reset time.

## Real-World Scenarios

### Cache Freshness Calculation

```http
GET /api/data HTTP/1.1
Host: api.example.com

HTTP/1.1 200 OK
Date: Sat, 18 Jan 2026 10:00:00 GMT
Cache-Control: max-age=600
Age: 120

{"data": "cached content"}

# Freshness calculation:
# Response age: 120 seconds (Age header)
# Max age: 600 seconds (Cache-Control)
# Remaining fresh: 480 seconds (600 - 120)
```

### Conditional Requests

```http
# Initial request
GET /resource HTTP/1.1

HTTP/1.1 200 OK
Date: Sat, 18 Jan 2026 10:00:00 GMT
Last-Modified: Sat, 18 Jan 2026 09:00:00 GMT
ETag: "abc123"

# Subsequent request
GET /resource HTTP/1.1
If-Modified-Since: Sat, 18 Jan 2026 09:00:00 GMT

HTTP/1.1 304 Not Modified
Date: Sat, 18 Jan 2026 10:05:00 GMT
```

### API Rate Limiting

```http
GET /api/endpoint HTTP/1.1

HTTP/1.1 200 OK
Date: Sat, 18 Jan 2026 12:00:00 GMT
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 950
X-RateLimit-Reset: Sat, 18 Jan 2026 13:00:00 GMT
```

### Time-Sensitive Operations

```http
POST /api/transactions HTTP/1.1

HTTP/1.1 201 Created
Date: Sat, 18 Jan 2026 15:30:45 GMT
Content-Type: application/json

{
  "id": "txn_123",
  "timestamp": "2026-01-18T15:30:45Z",
  "serverTime": "2026-01-18T15:30:45Z"
}
```

## Server Implementation

### Express.js (Node.js)

```javascript
const express = require('express')
const app = express()

// Express automatically sets Date header, but you can set it manually
app.use((req, res, next) => {
  // Custom Date header (rarely needed)
  res.setHeader('Date', new Date().toUTCString())
  next()
})

app.get('/api/data', (req, res) => {
  // Date is automatically set
  res.json({ data: 'example' })
})

// Calculate response age
app.get('/cached', (req, res) => {
  const cacheDate = new Date('2026-01-18T10:00:00Z')
  const now = new Date()
  const age = Math.floor((now - cacheDate) / 1000)

  res.setHeader('Date', now.toUTCString())
  res.setHeader('Age', age.toString())
  res.setHeader('Cache-Control', 'max-age=3600')

  res.json({ cached: true })
})

// Check clock skew
app.use((req, res, next) => {
  const clientDate = req.headers.date
  if (clientDate) {
    const clientTime = new Date(clientDate).getTime()
    const serverTime = Date.now()
    const skew = Math.abs(serverTime - clientTime) / 1000

    if (skew > 300) {
      // 5 minutes
      console.warn(`Clock skew detected: ${skew} seconds`)
    }
  }
  next()
})
```

### Custom Date Formatting

```javascript
function formatHTTPDate(date) {
  const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
  const months = [
    'Jan',
    'Feb',
    'Mar',
    'Apr',
    'May',
    'Jun',
    'Jul',
    'Aug',
    'Sep',
    'Oct',
    'Nov',
    'Dec'
  ]

  const day = days[date.getUTCDay()]
  const dateNum = String(date.getUTCDate()).padStart(2, '0')
  const month = months[date.getUTCMonth()]
  const year = date.getUTCFullYear()
  const hour = String(date.getUTCHours()).padStart(2, '0')
  const minute = String(date.getUTCMinutes()).padStart(2, '0')
  const second = String(date.getUTCSeconds()).padStart(2, '0')

  return `${day}, ${dateNum} ${month} ${year} ${hour}:${minute}:${second} GMT`
}

app.use((req, res, next) => {
  res.setHeader('Date', formatHTTPDate(new Date()))
  next()
})
```

### FastAPI (Python)

```python
from fastapi import FastAPI, Response
from datetime import datetime
from email.utils import formatdate
import time

app = FastAPI()

@app.middleware("http")
async def add_date_header(request, call_next):
    response = await call_next(request)

    # FastAPI sets Date automatically, but you can override
    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)

    return response

@app.get("/api/data")
async def get_data(response: Response):
    # Date is automatically set
    return {"data": "example"}

@app.get("/cached")
async def cached_response(response: Response):
    # Calculate age
    cache_time = datetime(2026, 1, 18, 10, 0, 0)
    now = datetime.utcnow()
    age = int((now - cache_time).total_seconds())

    response.headers["Date"] = formatdate(timeval=None, localtime=False, usegmt=True)
    response.headers["Age"] = str(age)
    response.headers["Cache-Control"] = "max-age=3600"

    return {"cached": True}
```

### Django

```python
from django.http import JsonResponse
from django.utils.http import http_date
import time

def api_view(request):
    # Django automatically sets Date header
    return JsonResponse({'data': 'example'})

def custom_date_view(request):
    response = JsonResponse({'data': 'example'})

    # Set custom Date (rarely needed)
    response['Date'] = http_date()

    return response

def cached_view(request):
    # Calculate age
    cache_timestamp = 1737198000  # Unix timestamp
    now = time.time()
    age = int(now - cache_timestamp)

    response = JsonResponse({'cached': True})
    response['Date'] = http_date()
    response['Age'] = str(age)
    response['Cache-Control'] = 'max-age=3600'

    return response
```

### Nginx

```nginx
server {
    listen 80;
    server_name example.com;

    # Nginx automatically adds Date header

    location /api/ {
        # Date is always added by default
        proxy_pass http://backend;
    }

    # You can't easily override Date in Nginx
    # It's automatically set to the current time
}
```

## Getting Date right

### For Servers

**1. Always include Date header**

```http
# ✅ Required in all responses
HTTP/1.1 200 OK
Date: Sat, 18 Jan 2026 10:30:00 GMT

# ❌ Missing Date (violates HTTP spec)
HTTP/1.1 200 OK
Content-Type: text/html
```

**2. Use correct GMT format**

```javascript
// ✅ Correct format
const date = new Date().toUTCString()
// "Sat, 18 Jan 2026 10:30:00 GMT"

// ❌ Wrong format
const date = new Date().toString()
// "Sat Jan 18 2026 10:30:00 GMT+0000"
```

**3. Keep server clocks synchronized**

```bash
# Use NTP to keep server time accurate
sudo timedatectl set-ntp true

# Check time synchronization
timedatectl status
```

**4. Don't manipulate Date for caching tricks**

```http
# ❌ Bad: Faking the Date
Date: Sat, 18 Jan 2025 10:30:00 GMT  (when it's actually 2026)

# ✅ Good: Use proper caching headers
Date: Sat, 18 Jan 2026 10:30:00 GMT
Cache-Control: max-age=31536000
```

**5. Include Age header for cached responses**

```http
# ✅ Clear cache status
Date: Sat, 18 Jan 2026 10:00:00 GMT
Age: 300
Cache-Control: max-age=3600
```

### For Clients

**1. Use Date for cache calculations**

```javascript
async function fetchWithCacheCheck(url) {
  const response = await fetch(url)

  const date = new Date(response.headers.get('Date'))
  const age = parseInt(response.headers.get('Age') || '0')
  const maxAge = parseCacheControl(response.headers.get('Cache-Control'))

  const responseAge = age
  const freshnessLifetime = maxAge
  const isFresh = responseAge < freshnessLifetime

  console.log('Response date:', date)
  console.log('Is fresh:', isFresh)

  return response
}
```

**2. Detect clock skew**

```javascript
fetch('/api/time').then((response) => {
  const serverDate = new Date(response.headers.get('Date'))
  const clientDate = new Date()
  const skew = Math.abs(serverDate - clientDate) / 1000

  if (skew > 60) {
    console.warn(`Clock skew detected: ${skew} seconds`)
  }
})
```

**3. Use for retry-after calculations**

```javascript
fetch('/api/endpoint').then((response) => {
  if (response.status === 429) {
    const serverDate = new Date(response.headers.get('Date'))
    const retryAfter = parseInt(response.headers.get('Retry-After'))
    const retryTime = new Date(serverDate.getTime() + retryAfter * 1000)

    console.log('Retry after:', retryTime)
  }
})
```

## Date Format Examples

### HTTP-date Format (RFC 9110 section 5.6.7)

```http
Date: Sat, 18 Jan 2026 10:30:00 GMT
```

### JavaScript Generation

```javascript
// Correct HTTP date format
const httpDate = new Date().toUTCString()
// "Sat, 18 Jan 2026 10:30:00 GMT"

// ISO format (not for Date header)
const isoDate = new Date().toISOString()
// "2026-01-18T10:30:00.000Z"
```

### Python Generation

```python
from email.utils import formatdate

# Correct HTTP date format
http_date = formatdate(timeval=None, localtime=False, usegmt=True)
# "Sat, 18 Jan 2026 10:30:00 GMT"
```

## Cache Freshness Calculation

### Formula

```text
response_age = Age header value OR (now - Date header value)
freshness_lifetime = max-age from Cache-Control
response_is_fresh = (response_age < freshness_lifetime)
```

### Example

```http
Date: Sat, 18 Jan 2026 10:00:00 GMT
Cache-Control: max-age=600
Age: 120

# Current time: Sat, 18 Jan 2026 10:02:00 GMT
# Response age: 120 seconds (from Age header)
# Freshness lifetime: 600 seconds (from max-age)
# Fresh for: 480 more seconds (600 - 120)
```

## Testing Date Header

### Using curl

```bash
# Check Date header
curl -I https://example.com

# Verbose output to see Date
curl -v https://example.com

# Extract only Date header
curl -s -I https://example.com | grep -i "^Date:"
```

### Using JavaScript

```javascript
// Check server date
fetch('https://api.example.com').then((response) => {
  const dateHeader = response.headers.get('Date')
  const serverTime = new Date(dateHeader)
  const clientTime = new Date()

  console.log('Server time:', serverTime)
  console.log('Client time:', clientTime)
  console.log('Difference:', Math.abs(serverTime - clientTime) / 1000, 'seconds')
})

// Calculate cache freshness
fetch('https://api.example.com/cached').then((response) => {
  const date = new Date(response.headers.get('Date'))
  const age = parseInt(response.headers.get('Age') || '0')
  const cacheControl = response.headers.get('Cache-Control')
  const maxAgeMatch = cacheControl.match(/max-age=(\d+)/)
  const maxAge = maxAgeMatch ? parseInt(maxAgeMatch[1]) : 0

  console.log('Response age:', age, 'seconds')
  console.log('Max age:', maxAge, 'seconds')
  console.log('Fresh for:', maxAge - age, 'seconds')
})
```

## Related Headers

- [Age](https://howhttpworks.com/headers/age) - How old the response is
- [Cache-Control](https://howhttpworks.com/headers/cache-control) - Caching directives
- [Expires](https://howhttpworks.com/headers/expires) - Absolute expiration time
- [Last-Modified](https://howhttpworks.com/headers/last-modified) - When resource was last changed
