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

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)

  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)
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

Overrides origin Cache-Control headers (except no-store/private, see below). 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)
Set-Cookie present TTL = forcedTTL value (header stripped)

Cannot override: Cache-Control: no-store and Cache-Control: private both cause Varnish to mark the response uncacheable before vcl_backend_response runs. This is a Varnish runtime limitation — beresp.uncacheable is write-once-to-true and cannot be reverted. In these cases the response is not cached regardless of forcedTTL.

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.

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

# 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:

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

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.