LogoPly
Middleware

ETag and Cache-Control

Add response validators and cache directives.

ETag and Cache-Control

Ply provides two small middleware helpers for HTTP caching.

app.Use(
	middleware.Compress(0),
	middleware.ETag(),
)

ETag computes a strong validator from the response body and answers conditional requests. CacheControl sets a fixed Cache-Control header.

ETag

app.Get("/article", func(c *core.Context) {
	c.Text(200, "article body")
})

The response includes:

ETag: "c5f4f3c9a8e1d2b7f6a5c4e3d2b1a0f9"

On the next request the client sends:

If-None-Match: "c5f4f3c9a8e1d2b7f6a5c4e3d2b1a0f9"

Ply returns 304 Not Modified with an empty body.

Behavior

  • Only GET and HEAD responses are processed.
  • Only 200 responses with a non-empty body receive an ETag.
  • Streaming responses are skipped.
  • An existing ETag set by the handler is preserved.
  • Weak validators (W/"...") match the strong tag for If-None-Match.
  • The tag is derived from the first 16 bytes of SHA-256, giving 32 hex characters.

CacheControl

app.Get("/assets/config.json", middleware.CacheControl("public, max-age=300"), func(c *core.Context) {
	c.Ctx.SendFile("public/config.json")
})

Use middleware for a shared policy:

app.Use(middleware.CacheControl("no-store"))
ValueUse
no-storeSensitive responses that must not be cached
no-cacheRevalidate before using a cached copy
public, max-age=60Shared cache for one minute
private, max-age=300Browser cache only

Ordering with compression

Place Compress before ETag in the middleware list:

app.Use(
	middleware.Compress(0),
	middleware.ETag(),
)

Compress wraps ETag, so the tag is computed from the uncompressed body and stays stable when the client changes Accept-Encoding.

If ETag wraps Compress, the tag is computed from the compressed bytes. That still works, but the validator changes with the encoding.

Caching correctness

ETag handles validators, not cache duration. Set Cache-Control separately.

Do not cache responses that depend on a user:

app.Get("/me", func(c *core.Context) {
	c.Ctx.Response.Header.Set("Cache-Control", "private, no-store")
	c.JSON(200, currentUser(c))
})

For shared caches, include Vary when the response changes by request header, and avoid caching authenticated responses unless the cache is configured to respect Authorization.