Skip to content

VarnishCachePolicy Reference

Overview

Varnish Gateway ships with caching disabled by default. Every request passes straight through to the backend — no caching, no request coalescing. This is the safe default for Kubernetes, where blue/green deployments, canary rollouts, and traffic splitting all assume the proxy faithfully forwards requests.

VarnishCachePolicy (VCP) is how you opt in to caching. Attaching a VCP to a Gateway or HTTPRoute says: "I understand caching, and I want it here." No VCP, no caching — simple, explicit, auditable.

Spec

```yaml apiVersion: gateway.varnish-software.com/v1alpha1 kind: VarnishCachePolicy metadata: name: my-cache-policy namespace: default spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute # or Gateway name: my-route # sectionName: my-rule # optional: target a specific named rule

# TTL — exactly one of defaultTTL or forcedTTL is required defaultTTL: 5m # forcedTTL: 1h

grace: 30s # serve stale while revalidating (default: 0) keep: 24h # serve stale when backend is down (default: 0) requestCoalescing: true # collapsed forwarding (default: true)

cacheKey: headers: - Accept-Language queryParameters: include: # allowlist (mutually exclusive with exclude) - page - filter # exclude: # denylist # - utm_source

bypass: headers: - name: Authorization - name: Cookie valueRegex: "session_id|admin_token" ```

Fields

Field Type Default Description
targetRef PolicyTargetReference required Gateway, HTTPRoute, or HTTPRoute rule to attach to
targetRef.sectionName string Name of a specific rule within the targeted HTTPRoute
defaultTTL Duration required* TTL when origin sends no Cache-Control. Origin headers take precedence
forcedTTL Duration required* Forced TTL, ignores origin Cache-Control entirely
grace Duration 0 Serve stale while revalidating (equivalent to stale-while-revalidate)
keep Duration 0 Serve stale when backend is down (equivalent to stale-if-error)
requestCoalescing bool true Collapsed forwarding for concurrent requests to the same uncached object
cacheKey.headers []string [] Request headers to include in cache key
cacheKey.queryParameters.include []string all Allowlist of query params in cache key
cacheKey.queryParameters.exclude []string none Denylist of query params from cache key
bypass.headers []HeaderCondition [] Headers that trigger cache bypass

*Exactly one of defaultTTL or forcedTTL must be set.

Hierarchy and Inheritance

VCP is an Inherited Policy per Gateway API conventions. The most specific policy wins:

Gateway ← VCP provides defaults for all routes through this gateway └── HTTPRoute ← VCP overrides gateway defaults for all rules in this route └── Rule (named) ← VCP overrides route defaults for this specific rule

  • No VCP anywhere: caching disabled (pass mode)
  • VCP on Gateway only: all routes through that gateway use the gateway policy
  • VCP on HTTPRoute: all rules in that route use this policy, ignoring gateway defaults
  • VCP on a named rule: that rule uses its own policy; other rules fall back to the HTTPRoute-level or Gateway-level VCP

Override is complete replacement, not field-level merging. An HTTPRoute VCP does not inherit the gateway VCP's grace or cacheKey settings — it uses exactly what you specified.

defaultTTL vs forcedTTL

The two TTL modes encode different trust relationships with your origin:

defaultTTL — Origin wins

Used when the origin doesn't send Cache-Control headers. Origin headers always take precedence.

Origin sends Result
No Cache-Control TTL = defaultTTL value
Cache-Control: max-age=60 TTL = 60s (origin wins)
Cache-Control: s-maxage=120 TTL = 120s (origin wins)
Cache-Control: no-store Not cached (origin wins)
Cache-Control: private Not cached (origin wins)
Set-Cookie present Not cached (Varnish default)

forcedTTL — Operator wins

Ignores all origin Cache-Control headers. Use when you know better than the origin.

Origin sends Result
No Cache-Control TTL = forcedTTL value
Cache-Control: max-age=60 TTL = forcedTTL value (ignored)
Cache-Control: no-store TTL = forcedTTL value (ignored)
Set-Cookie present TTL = forcedTTL value (header stripped)

Status and Conditions

VCP reports status following Gateway API policy conventions:

Condition Reason Meaning
Accepted=True Accepted Policy is valid and active
Accepted=False TargetNotFound Referenced HTTPRoute/Gateway doesn't exist
Accepted=False Invalid Spec validation failed (e.g., include+exclude both set)
Accepted=False Conflicted Another VCP already targets the same route

Conflict resolution: if two VCPs target the same HTTPRoute, the oldest (by creation timestamp) wins.

Examples

Cache Static Assets

Two HTTPRoutes serve the same hostname. Static assets get cached; the API does not.

```yaml apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: static-assets spec: parentRefs: - name: my-gateway hostnames: - www.example.com rules: - matches: - path: { type: PathPrefix, value: /static } backendRefs: - name: cdn-origin port: 80


apiVersion: gateway.varnish-software.com/v1alpha1 kind: VarnishCachePolicy metadata: name: cache-static spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: static-assets defaultTTL: 1h grace: 5m ```

Result: /static/logo.png is cached for 1h with 5m stale-while-revalidate. API routes without a VCP remain in pass mode.

Gateway-Wide Defaults with Per-Route Override

```yaml

Conservative caching for all routes through this gateway

apiVersion: gateway.varnish-software.com/v1alpha1 kind: VarnishCachePolicy metadata: name: gateway-defaults spec: targetRef: group: gateway.networking.k8s.io kind: Gateway name: my-gateway defaultTTL: 60s grace: 10s bypass: headers: - name: Authorization - name: Cookie


Product catalog gets aggressive caching (full replacement — no inheritance)

apiVersion: gateway.varnish-software.com/v1alpha1 kind: VarnishCachePolicy metadata: name: cache-catalog spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: product-catalog defaultTTL: 30m grace: 1h keep: 24h cacheKey: headers: - Accept-Language queryParameters: include: - page - category ```

The product-catalog route uses 30m TTL with no bypass rules. The gateway-level Authorization/Cookie bypass does not apply — the route VCP is a complete replacement.

Per-Rule Targeting

Different caching per rule within a single HTTPRoute, using sectionName:

```yaml apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: my-app spec: parentRefs: - name: my-gateway hostnames: - www.example.com rules: - name: static-assets matches: - path: { type: PathPrefix, value: /static } backendRefs: - name: cdn-origin port: 80 - name: api matches: - path: { type: PathPrefix, value: /api } backendRefs: - name: api-server port: 8080 - name: pages matches: - path: { type: PathPrefix, value: / } backendRefs: - name: web-server port: 80


apiVersion: gateway.varnish-software.com/v1alpha1 kind: VarnishCachePolicy metadata: name: cache-static spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: my-app sectionName: static-assets forcedTTL: 24h


apiVersion: gateway.varnish-software.com/v1alpha1 kind: VarnishCachePolicy metadata: name: cache-pages spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: my-app sectionName: pages defaultTTL: 5m grace: 30s bypass: headers: - name: Cookie valueRegex: "session_id" ```

Result: - /static/* — forced 24h TTL (origin headers ignored) - /api/* — no VCP, pass mode (no caching) - /* — 5m default TTL, respects origin Cache-Control, bypasses for session cookies

UTM Parameter Stripping

yaml apiVersion: gateway.varnish-software.com/v1alpha1 kind: VarnishCachePolicy metadata: name: cache-marketing spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: marketing-site defaultTTL: 15m grace: 1h keep: 6h cacheKey: headers: - Accept-Language queryParameters: exclude: - utm_source - utm_medium - utm_campaign - utm_content - utm_term - fbclid - gclid

/pricing?utm_source=google and /pricing?utm_source=twitter share the same cache entry.

Interaction with Traffic Splitting

Caching and weighted traffic splitting are in tension. If a route with multiple weighted backends has a VCP attached, the first response gets cached and all subsequent requests serve that cached version — the weight split becomes meaningless for cached paths.

Recommendation: Don't attach a VCP to routes with active traffic splitting unless the backends return identical content.