Skip to main content

Overview

The surrogate middleware manages Surrogate-Control and Surrogate-Key headers for CDN cache control (Fastly, Varnish, etc.). Use it when you need:
  • CDN cache purging
  • Edge cache control
  • Cache tagging

Installation

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

Quick Start

app := mizu.New()

app.Use(surrogate.New())

app.Get("/article/:id", func(c *mizu.Ctx) error {
    surrogate.Key(c, "article-"+c.Param("id"))
    return c.JSON(200, article)
})

Configuration

Options

OptionTypeDefaultDescription
MaxAgetime.Duration0Surrogate max-age
StaleWhileRevalidatetime.Duration0Stale-while-revalidate

Examples

Set Surrogate Keys

app.Get("/article/:id", func(c *mizu.Ctx) error {
    surrogate.Key(c, "article-"+c.Param("id"))
    surrogate.Key(c, "articles")
    return c.JSON(200, article)
})
// Surrogate-Key: article-123 articles

Surrogate Control

app.Use(surrogate.WithOptions(surrogate.Options{
    MaxAge:               time.Hour,
    StaleWhileRevalidate: 10 * time.Minute,
}))
// Surrogate-Control: max-age=3600, stale-while-revalidate=600

Per-Route Control

app.Get("/dynamic", func(c *mizu.Ctx) error {
    surrogate.Control(c, "no-store")
    return c.JSON(200, data)
})

app.Get("/static", func(c *mizu.Ctx) error {
    surrogate.Control(c, "max-age=86400")
    return c.JSON(200, data)
})

API Reference

Functions

// New creates surrogate middleware
func New() mizu.Middleware

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

// Key adds surrogate key
func Key(c *mizu.Ctx, keys ...string)

// Control sets surrogate control
func Control(c *mizu.Ctx, control string)

CDN Integration

Fastly

Surrogate-Key: article-123 homepage
Surrogate-Control: max-age=3600

Varnish

Uses same headers with xkey support.

Technical Details

The surrogate middleware implements CDN cache control through a context-based key management system:

Architecture

  • Context Storage: Uses Go’s context.Context to store surrogate keys during request processing
  • Keys Structure: Maintains a Keys type that holds a slice of string keys
  • Header Management: Automatically sets headers after handler execution using middleware pattern

Implementation Details

  1. Initialization: The middleware creates a Keys instance and stores it in the request context
  2. Default Keys: If configured, default keys are added to every response
  3. Handler Execution: The next handler in the chain executes, allowing route handlers to add keys via Add()
  4. Header Setting: After handler completion, the middleware:
    • Joins all keys with spaces and sets the configured header (default: Surrogate-Key)
    • Builds and sets the Surrogate-Control header based on options (MaxAge, StaleWhileRevalidate, StaleIfError)

Control Header Format

The Surrogate-Control header is built from options:
  • max-age=N: Set via Options.MaxAge
  • stale-while-revalidate=N: Set via Options.StaleWhileRevalidate
  • stale-if-error=N: Set via Options.StaleIfError
All directives are combined with comma-space separation.

CDN Presets

The middleware includes three CDN-specific presets:
  • Fastly(): Uses Surrogate-Key header
  • Varnish(): Uses xkey header
  • CloudFront(): Uses x-amz-meta-surrogate-key header
Each preset configures the appropriate headers for the CDN while maintaining the same API.

Best Practices

  • Use meaningful, granular keys
  • Group related content with shared keys
  • Set appropriate TTLs
  • Plan purge strategy

Testing

The surrogate middleware includes comprehensive test coverage for all functionality:
Test CaseDescriptionExpected Behavior
TestNewBasic middleware initialization with key additionAdds multiple keys (page, content) and sets Surrogate-Key header
TestWithOptions_DefaultKeysDefault keys configurationAutomatically includes default keys (site, global) in all responses
TestWithOptions_CustomHeaderCustom header nameUses custom header name (xkey) instead of default Surrogate-Key
TestGetRetrieve Keys from contextReturns the Keys instance from context and allows manipulation
TestAddAdd multiple keys in handlerAdds multiple keys (key1, key2) and includes both in header
TestClearClear all keys including defaultsRemoves all keys, resulting in no Surrogate-Key header
TestWithKeysConvenience function for default keysCreates middleware with default keys using WithKeys() helper
TestFastlyFastly CDN presetUses Surrogate-Key header with Fastly configuration
TestVarnishVarnish CDN presetUses xkey header for Varnish compatibility
TestNoKeysNo keys added scenarioDoes not set Surrogate-Key header when no keys are added
TestKeysClearedKeys.Clear() methodProperly clears keys from the Keys struct
  • cache - Cache-Control headers
  • etag - ETag generation
  • vary - Vary headers