Skip to content
OCM LABS

How to use Cloudflare with Varnish for Magento

Stock Magento VCL tells Cloudflare not to cache HTML. Export a Cloudflare-aware VCL from Magento, syntax-check it, load it and verify the headers arrive.

Shane Blandford
Shane Blandford · FOUNDERPUBLISHED · 7 MIN READ

Put Cloudflare in front of a Magento store that already runs Varnish and you get cf-cache-status: DYNAMIC on every page, forever. Nothing is wrong on the Cloudflare side. The stock Magento Varnish configuration is doing what it was written to do, and that defeats any cache in front of it.

Here is why, what OCM Labs Cloudflare (free and open source) changes when it detects Varnish, how to export and load the Cloudflare-aware VCL, and how to prove the headers survive the trip. If edge caching is not on yet, read how to cache Magento HTML at the Cloudflare edge first; this post assumes those settings are in place.

Why stock Magento VCL blocks the edge

Magento's generated VCL does two things in vcl_deliver that matter here:

  1. It rewrites Cache-Control to no-cache on every response that leaves Varnish.
  2. It removes the tag header before the response goes out.

Both make sense when Varnish is the last cache before the browser. But Cloudflare reads the same response. It sees no-cache and no Cache-Tag, and stores nothing. HTML is served by Varnish only, and Cloudflare adds latency without adding a cache.

The fix is not a Cloudflare rule that forces caching; that would also cache whatever Varnish marked private. The fix is a VCL that lets the right responses through with the right headers, which is what the module exports.

What changes in Varnish mode

The module reads Magento's caching application setting at Stores > Configuration > Advanced > System > Full Page Cache. When it is set to Varnish, the module changes how it works on every response:

BUILT-IN FPC MODEVARNISH MODE
CACHE-CONTROL REWRITE IN PHPYesNo, left to Varnish
SESSION COOKIE STRIPPING IN PHPYesNo, left to Varnish
EDGE MARKER (INTERNAL TTL HEADER) ADDEDYesYes
CACHE-TAG HEADER ADDEDYesYes
WHO MAKES THE EDGE DECISIONMagentoThe VCL

Magento's own headers and cookies are left to Varnish, because Varnish already rewrites Cache-Control and strips session cookies. The module still adds its edge marker and the Cache-Tag header to responses Magento considers public, so the VCL can see them and decide what reaches Cloudflare.

Two limits apply in Varnish mode:

  • Edge caching covers default-context pages only. Visitors on another store view, currency or customer group carry the X-Magento-Vary cookie and are served by Varnish, not the edge.
  • Vary Handling must stay at Bypass. The Cache key option is not supported behind Varnish.

Step 1: Export the VCL

Run the export from the Magento root, naming the Varnish version you run and where to write the file:

bin/magento ocmlabs:cloudflare:vcl:export --varnish-version=6 --output=/tmp/default.vcl

--varnish-version accepts 4, 5, 6 or 7; 6 is the default. --output (short form -o) writes to a file; leave it off and the VCL goes to standard output, handy for a diff against your current file. The command exits 1 on any error.

From the admin, the Download Cloudflare VCL button on the Health tab produces the same file.

What you get is Magento's own generated VCL for that version, with two additions and nothing else changed.

Step 2: Syntax-check it

Before the file goes anywhere near a running Varnish, compile it:

varnishd -C -f /tmp/default.vcl > /dev/null

varnishd -C compiles the VCL and prints the result; sending that to /dev/null leaves only the errors, if any. A clean exit means the file parses for the Varnish version on that box. A mismatch between --varnish-version and the installed varnishd shows up here, not during a reload.

Step 3: Load it

Loading is the same as any VCL change on your setup: put the exported file where your Varnish reads its configuration, then reload the service or use varnishadm to load and switch to it. Follow the procedure you already use for Magento VCL changes. The exported file replaces the stock one; it is not a patch on top of it.

What the two additions do

In vcl_recv, a note is made of whether the request carries the X-Magento-Vary cookie. Nothing else happens there; the note is read later.

In vcl_deliver, a response that carries the module's edge marker gets Cache-Control: public, max-age=0, s-maxage=<ttl> instead of Magento's no-cache rewrite, with the TTL taken from the marker. Cache-Tag is left in place for Cloudflare. The marker itself is removed before the response leaves Varnish, so visitors never see it.

The marker branch is guarded. It skips:

  • any response that is already private or no-store
  • anything Varnish itself marked uncacheable
  • any request that carried the vary cookie, per the note from vcl_recv

The rule behind the guards: a page Varnish would not cache is never made cacheable at the edge. Only pages Varnish stored, and Magento marked public, get the edge headers.

Varnish keeps old objects

Varnish holds on to objects it stored before a configuration change until they expire or are purged. Pages already in Varnish still carry the headers they were stored with. The same applies after editing Bypass Paths or turning edge caching off. So after any of those changes, purge Varnish as well as Cloudflare:

bin/magento cache:clean full_page

With Purge Everything on Full Page Cache Clean turned on, that one command clears both. How the purge side works, including the queue and the purge log, is in how to purge Cloudflare cache from Magento.

Step 4: Verify from Magento

The "Origin sends edge cache headers" health check fetches a page straight from the address in Storefront Origin and reports whether Cache-Control and Cache-Tag arrive intact. For this check to tell you anything about Varnish, set Storefront Origin to the Varnish address, not the web server behind it. Run it from the Health tab or from the shell:

bin/magento ocmlabs:cloudflare:health

A pass means the exported VCL is loaded and passing the headers through. A fail after you loaded the VCL means something else between Varnish and Cloudflare is still stripping them: a second proxy, a load balancer, or a web server rule. The other 24 checks and what to do when they fail are covered in how to run Cloudflare health checks for Magento.

Step 5: Verify with curl

For a second opinion, ask Varnish directly, bypassing Cloudflare:

curl -sI -H "Host: www.example.com" http://varnish-host/some-category.html | grep -iE "cache-control|cache-tag"

You should see Cache-Control: public, max-age=0, s-maxage=... and a Cache-Tag line. If you still see no-cache, the old VCL is still active, or the guards excluded the page; try a plain category page with no X-Magento-Vary cookie. Once Varnish is right, request the page through Cloudflare twice and look for cf-cache-status: HIT on the second request. Cloudflare strips Cache-Tag from what it sends to visitors, so its absence on the public URL is expected.

If you would rather leave the VCL alone

Changing VCL on a production box is not always your call. Leave Enable Edge Caching of HTML off and skip this whole page. Purging, the cache rules, the health checks and analytics all keep working; only HTML stays on Varnish instead of the edge. Come back to the export when the VCL change is approved.

The full reference for this feature, including the exact options and exit codes, is on the Varnish docs page.

If you want someone to review your Varnish and Cloudflare layout before you touch production, that is what our performance and infrastructure work covers.

FAQ

Why does Cloudflare show DYNAMIC on every page when Varnish is in front of Magento?+
Magento's stock VCL rewrites Cache-Control to no-cache and removes the tag header in vcl_deliver before the response leaves Varnish. Cloudflare sees nothing cacheable and serves every HTML request from the origin. Loading the exported Cloudflare-aware VCL fixes this.
Does the module detect Varnish on its own?+
Yes. It reads Magento's caching application setting under Stores, Configuration, Advanced, System, Full Page Cache. When that is set to Varnish, the module stops rewriting headers and stripping cookies in PHP, keeps adding the edge marker and Cache-Tag, and leaves the edge decision to the VCL.
Which Varnish versions does the export support?+
The export accepts 4, 5, 6 or 7 through the --varnish-version option, with 6 as the default. Run varnishd -C -f against the exported file on the target box before loading it, so a version mismatch shows up as a compile error rather than a failed reload.
Can I use Vary Handling Cache key with Varnish?+
No. Behind Varnish the Cache key option is not supported, so leave Vary Handling at Bypass. Edge caching in Varnish mode covers default-context pages only; visitors carrying the X-Magento-Vary cookie are served by Varnish rather than the edge.
Why are old pages still served after I loaded the new VCL?+
Varnish keeps objects it stored before the change until they expire or are purged, and those objects carry the old headers. Run bin/magento cache:clean full_page after loading the VCL, after editing Bypass Paths, and after turning edge caching off. With Purge Everything on Full Page Cache Clean turned on, that purges Cloudflare too.
Do I have to change the VCL to use the module with Varnish?+
No. Leave Enable Edge Caching of HTML off and the module works without touching Varnish. Purging, cache rules, health checks and analytics all run as normal; HTML is simply served by Varnish instead of the Cloudflare edge until you decide to load the exported file.
Shane Blandford

Shane Blandford

FOUNDER

Founder of Orange Collar Media, a Denver ecommerce agency behind 800+ Magento and Shopify builds. In Magento since version 1.x - building, rescuing, and supporting revenue-critical stores since 2008.

All posts by Shane Blandford→

KEEP READING

Related from the Journal.

Your store can sell more. Let's find out how much.

Start a project