Varnish transforms a media site: 90% cache hits, origin nearly idle, CPU bill cut by three. Then an editor logs in, publishes an urgent fix — and anonymous users still see the old headline because purge hit only the article URL, not the home tagged listing-news.
This scenario illustrates Varnish's nature: not managed CDN, but an HTTP policy engine. You write VCL for who is identical, who differs, and what happens when a session_id cookie appears. Mishandled, it is the fastest cache for serving the wrong truth.
Before Varnish, the question is not "how much RAM?" but "is a logged-in user a cache variant or an exception?"
Mental model: hash, pass, miss
| VCL decision | Effect |
|---|---|
return (hash) | Lookup/store cache |
return (pass) | Origin, no store |
return (pipe) | Raw tunnel |
return (synth) | Synthetic response (maintenance) |
Typical flow: request → vcl_recv; session cookie → pass; POST/PUT → pass; else → hash key URL + lang + anon segment; hit → deliver; miss → origin → TTL in vcl_backend_response.
Varnish forces you to answer: is a logged-in user a cache variant or an exception?
Logged-in users: three patterns
Pattern 1 — full bypass if session cookie
if (req.http.Cookie ~ "PHPSESSID") {
return (pass);
}
Simple, safe, higher origin load for members.
Pattern 2 — hash segmentation
Same URL, two entries role=member vs anon. Risk: too many variants.
Pattern 3 — ESI (Edge Side Includes)
Cacheable shell plus <esi:include src="/private/header"> fragment in pass. Powerful, complex.
PURGE, BAN, and Surrogate-Keys
| Operation | Scope | Use |
|---|---|---|
| PURGE url | One object | Edited article |
| BAN expression | Pattern | ban("req.url ~ /news/") |
| Surrogate-Key ban | Logical tag | ban("obj.http.Surrogate-Key ~ article-123") |
Origin header: Surrogate-Key: article-123 listing-home. On CMS publish → ban tag article-123 plus listing-home. Without tags, manual PURGE—guaranteed forget.
TTL, grace, and stale
TTL: nominal freshness. Grace: serve stale while origin slow—useful peaks, dangerous for stock prices. For e-commerce: short or no grace on price pages.
Hosting and sizing
Varnish is RAM-hungry: plan ~1–2 GB plus cached object size. On VPS: dedicated instance if hit ratio > 70%; watch varnishstat—cache_hit, n_lru_nuked. Shared hosting: no Varnish; partial CDN edge without fine VCL control. See Dynamic page cache and CDN or origin optimization.
The peak: Varnish rewards those who know their audience
Decide and move forward without blind spots
Inventory cookies and mandatory pass routes, version VCL in Git with tested reload, wire Surrogate-Keys from CMS, test anonymous versus member on home, account and cart, then monitor hit ratio and ban failures before treating the cache layer as production-ready for all live traffic and editorial workflows.
Frequently asked questions
Does Varnish cache pages with session cookie?
By default no; configure pass or segmented hash—never member page to anonymous.
Difference pass, pipe, and cache?
cache stores; pass origin without store; pipe raw tunnel.
How to purge after update?
PURGE/BAN or CMS-automated Surrogate-Key ban.
Varnish in front of nginx or reverse?
Classic Varnish front → nginx → PHP-FPM.
Varnish speeds what you decided is identical—the rest is your VCL responsibility.