REST Lens
← Back to All Rules
Rule 58

Last-Modified

Category: HTTP Headers

Description

GET responses should include the Last-Modified header to enable efficient caching and conditional requests.

What It Checks

This rule validates that all successful GET responses (2XX status codes) include the Last-Modified header. It checks:

  • All GET operations in your OpenAPI specification
  • Only successful responses (status codes starting with "2")
  • The presence of the Last-Modified header in response definitions
  • All endpoints that retrieve resource data

Why It Matters

The Last-Modified header is essential for efficient HTTP caching and client-server synchronization:

  • Caching efficiency: Enables browsers and intermediary caches to determine if cached content is still fresh
  • Bandwidth optimization: Supports conditional requests with If-Modified-Since, reducing unnecessary data transfer
  • Performance: Allows clients to skip re-downloading unchanged resources, improving response times
  • Cache validation: Provides a reliable mechanism for cache invalidation and revalidation

Examples

Good ✅

paths:
  /users/{userId}:
    get:
      summary: Get a user
      responses:
        '200':
          description: User details
          headers:
            Last-Modified:
              description: The date and time the resource was last modified
              schema:
                type: string
                format: date-time
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'

  /products/{productId}:
    get:
      summary: Get product details
      responses:
        '200':
          description: Product information
          headers:
            Last-Modified:
              description: When the product was last updated
              schema:
                type: string
                format: date-time
            ETag:
              description: Entity tag for cache validation
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'

  /articles:
    get:
      summary: List articles
      responses:
        '200':
          description: Array of articles
          headers:
            Last-Modified:
              description: When the collection was last modified
              schema:
                type: string
                format: date-time
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Article'

Bad ❌

paths:
  /users/{userId}:
    get:
      summary: Get a user
      responses:
        '200':
          description: User details
          # Missing Last-Modified header
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'

  /products/{productId}:
    get:
      summary: Get product details
      responses:
        '200':
          description: Product information
          headers:
            ETag:
              schema:
                type: string
            # Has ETag but missing Last-Modified
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'

  /articles:
    get:
      summary: List articles
      responses:
        '200':
          description: Array of articles
          # No caching headers at all
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Article'

Configuration

This rule has no configuration options. It is either enabled or disabled.

How to Fix

  1. Add Last-Modified header to all GET responses:

    responses:
      '200':
        description: Success
        headers:
          Last-Modified:
            description: The date and time the resource was last modified
            schema:
              type: string
              format: date-time
    
  2. Implement server-side tracking:

    • Store updated_at or modified_at timestamps in your database
    • Include these timestamps in the Last-Modified header value
    • Use RFC 7231 format: Wed, 21 Oct 2015 07:28:00 GMT
  3. Support conditional requests (recommended):

    parameters:
      - name: If-Modified-Since
        in: header
        description: Only return the resource if modified after this date
        schema:
          type: string
          format: date-time
    responses:
      '200':
        description: Resource has been modified
        headers:
          Last-Modified:
            schema:
              type: string
              format: date-time
      '304':
        description: Not Modified - resource unchanged since If-Modified-Since
    
  4. Consider combining with ETag:

    • Use both Last-Modified and ETag headers for robust caching
    • Last-Modified for time-based validation
    • ETag for content-based validation
  5. Handle collection resources appropriately:

    • For collections, use the most recent modification time of any item
    • Or use the time the collection itself was last changed (items added/removed)

Related Rules

  • Rule 59: ETag Usage - Complementary cache validation mechanism
  • Rule 60: Cache-Control Headers - General caching directives
  • Rule 37: HTTP Status Codes - Including 304 Not Modified for conditional requests