Skip to main content

Overview

The etag middleware automatically generates ETag headers for responses, enabling efficient HTTP caching with conditional requests. When a client sends a matching If-None-Match header, it returns 304 Not Modified. Use it when you need:
  • Bandwidth optimization through caching
  • Conditional GET requests
  • Cache validation

Installation

import "github.com/go-mizu/mizu/middlewares/etag"

Quick Start

app := mizu.New()

// Enable ETag generation
app.Use(etag.New())

app.Get("/api/data", func(c *mizu.Ctx) error {
    return c.JSON(200, data)
})

Configuration

Options

OptionTypeDefaultDescription
WeakboolfalseGenerate weak ETags
HashFuncfunc([]byte) stringMD5 hashCustom hash function

Examples

Strong ETags (Default)

app.Use(etag.New())
// Generates: ETag: "abc123def456"

Weak ETags

app.Use(etag.Weak())
// Generates: ETag: W/"abc123def456"

Custom Hash Function

app.Use(etag.WithOptions(etag.Options{
    HashFunc: func(body []byte) string {
        h := sha256.Sum256(body)
        return hex.EncodeToString(h[:16])
    },
}))

How It Works

  1. Request comes in without If-None-Match
  2. Response is generated with ETag header
  3. Client caches response with ETag
  4. Next request includes If-None-Match: "etag-value"
  5. If content unchanged, returns 304 Not Modified
  6. If changed, returns new response with new ETag

API Reference

Functions

// New creates ETag middleware with defaults
func New() mizu.Middleware

// Weak creates middleware generating weak ETags
func Weak() mizu.Middleware

// WithOptions creates middleware with configuration
func WithOptions(opts Options) mizu.Middleware

Behavior

  • Only generates ETags for GET and HEAD requests
  • Skips error responses (4xx, 5xx)
  • Returns 304 Not Modified for matching ETags
  • Supports wildcard If-None-Match: *
  • Same content always produces same ETag

Strong vs Weak ETags

TypeFormatUse Case
Strong"abc123"Byte-for-byte identical
WeakW/"abc123"Semantically equivalent

Technical Details

The ETag middleware implementation uses a buffered writer approach to intercept and process responses:

Response Buffering

The middleware buffers the entire response body using a custom bufferedWriter that wraps the original http.ResponseWriter. This allows the middleware to:
  • Calculate the hash of the complete response body
  • Determine the final status code
  • Conditionally return 304 Not Modified before sending the full response

Hash Generation

By default, the middleware uses CRC32 (IEEE polynomial) for hash generation, which provides:
  • Fast computation suitable for HTTP caching
  • Sufficient uniqueness for cache validation
  • Low memory overhead
The hash function can be customized via HashFunc option to use alternative algorithms like SHA256 for enhanced security or collision resistance.

ETag Format

ETags are formatted according to RFC 7232:
  • Strong ETags: "<hash>" - Indicates byte-for-byte identical content
  • Weak ETags: W/"<hash>" - Indicates semantically equivalent content

Conditional Request Handling

The middleware checks the If-None-Match header from the client:
  • If it matches the generated ETag, returns 304 Not Modified with the ETag header
  • If it’s a wildcard (*), always returns 304 Not Modified
  • If no match, proceeds with the full response including the ETag header

Request Method Filtering

The middleware only processes GET and HEAD requests, as these are the only methods where conditional requests are applicable according to HTTP specifications.

Status Code Handling

ETags are only generated for successful responses (2xx status codes) with non-empty body content. Error responses (4xx, 5xx) are not cached and do not receive ETag headers.

Best Practices

  • Use strong ETags for static files
  • Use weak ETags for dynamic content that may vary slightly
  • Combine with Cache-Control for full caching strategy
  • Place after compression middleware

Testing

The middleware includes comprehensive test coverage for all functionality:
Test CaseDescriptionExpected Behavior
TestNew/generates_ETagBasic ETag generation for GET requestReturns 200 OK with quoted ETag header
TestNew/returns_304_for_matching_If-None-MatchClient sends matching If-None-Match headerReturns 304 Not Modified
TestNew/returns_200_for_non-matching_If-None-MatchClient sends non-matching If-None-MatchReturns 200 OK with full response
TestNew/wildcard_If-None-MatchClient sends If-None-Match: *Returns 304 Not Modified
TestWeakWeak ETag generationETag header starts with W/"
TestWithOptions_CustomHashCustom hash function providedUses custom hash in ETag
TestNew_SkipsNonGetHeadPOST request madeNo ETag header generated
TestNew_SkipsErrorResponses500 error responseNo ETag header generated
TestNew_ConsistentETagSame content returned twiceBoth responses have identical ETags
TestNew_DifferentContentDifferent content returnedDifferent ETags generated
TestNew_HEADHEAD request madeProcesses request without errors