Skip to content
PERFORMANCE

Why put Cloudflare in front of Magento: what a CDN does for a store, what it does not, and what breaks

What Cloudflare does for a Magento store: a CDN for static files, edge-cached HTML, DDoS and bot protection, a WAF, and the five settings that break checkout.

Shane Blandford
Shane Blandford · FOUNDERPUBLISHED · 15 MIN READ

A Magento CDN question usually arrives as a speed question. The store feels slow, someone runs a page speed tool, the report says "serve static assets from a CDN", and Cloudflare is the first name that comes up because it is free. That is a fine reason to start. It is also the smallest of the reasons to put Cloudflare in front of a Magento store, and if static files are all you set up, you will leave most of the value on the table and pick up a couple of ways to break checkout.

This is the long version: what Cloudflare is when it sits in front of Magento, what you get before you cache anything, how to handle static files, why HTML is the part that matters, what the security side buys you, and the specific settings that break Magento. It applies to self-hosted Magento Open Source and Adobe Commerce. Adobe Commerce on cloud infrastructure ships with its own CDN and is a different conversation.

What Cloudflare is when it sits in front of Magento

Cloudflare is a reverse proxy. You move your DNS to Cloudflare, mark the storefront record as proxied (the orange cloud), and from then on a visitor who looks up your hostname gets a Cloudflare anycast address instead of your server's address. Every request lands on the nearest Cloudflare data center first, and Cloudflare opens its own connection to your origin to fetch whatever it cannot answer itself.

Three things follow from that, and they are the reason this is worth doing even on a store that never caches a single page:

  • Your origin's IP address is no longer public. Anyone who wants to attack the server has to find it first.
  • TLS terminates at the edge, close to the visitor. The handshake happens over a short hop, and the edge speaks HTTP/2 and HTTP/3 to the browser whether or not your nginx does.
  • Every request passes through Cloudflare's filters before it reaches PHP. Whatever you turn on there (DDoS mitigation, a WAF, bot challenges, rate limits) runs on Cloudflare's hardware, not yours.

One caveat on the first point. Hiding the origin only works if the origin refuses direct connections. Cloudflare publishes its IP ranges; restrict your web server or security group to those, and turn on Authenticated Origin Pulls, which is available on every plan and makes the origin accept only TLS connections that carry Cloudflare's client certificate. Without that, a bot that finds the old IP in a DNS history service talks straight to Magento and skips everything below.

What you get on day one, before caching anything

The free plan, with the orange cloud on and nothing else configured, gives a Magento store:

  • Unmetered DDoS protection at layers 3 through 7. Cloudflare's availability table lists it as included on the Free plan. A volumetric attack is absorbed by their network, not your ALB.
  • The Free Managed Ruleset for the WAF, which Cloudflare describes as targeting high-impact, widely exploited vulnerabilities. The fuller Cloudflare Managed Ruleset and the OWASP Core Ruleset start on Pro.
  • Bot Fight Mode, which challenges requests matching known bot patterns. It is all or nothing on the free plan and cannot be scoped to paths, so read the section on what breaks before you leave it on.
  • One rate limiting rule, keyed on IP over a 10 second window. One is enough for the Magento login endpoint.
  • Modern transport: HTTP/3, Brotli compression, TLS 1.3 and automatic certificate renewal. Early Hints, the 103 response that lets the browser start fetching CSS and fonts while Magento is still rendering, is there too, but Cloudflare builds it from Link: rel=preload headers your origin sends, and stock Magento sends none, so it needs theme work to do anything.
  • Tiered Cache, which routes cache misses through an upper-tier data center before they reach your origin, so fewer Cloudflare locations open connections to your server.

None of that touches Magento's code. The one Magento-side change you must make on day one is the real visitor IP: with Cloudflare in front, REMOTE_ADDR is a Cloudflare address, so order records, fraud screening and admin IP allowlists all see Cloudflare instead of the shopper. Restore the address from CF-Connecting-IP at the web server (nginx's real_ip module, with Cloudflare's published ranges as the trusted set) or with a module that validates the peer before trusting the header.

Static files: the Magento CDN most people mean

Magento serves two kinds of static content. Everything under /static/ is theme and JavaScript output from setup:static-content:deploy, with a version number in the URL when static file signing is on. Everything under /media/ is product images, including the resized cache under /media/catalog/product/cache/, plus WYSIWYG uploads and downloadable product samples.

Cloudflare caches files by extension, and the default list covers images, CSS, JavaScript, fonts and PDFs. So the moment the orange cloud is on, a product image requested twice from the same region is served from the edge the second time. You can confirm it with a single header:

curl -sI https://www.example.com/static/version1730000000/frontend/Magento/luma/en_US/css/styles-m.css | grep -i cf-cache-status

HIT on the second request is what you want. MISS is the first fill. DYNAMIC means Cloudflare decided the asset was not eligible, usually because the origin sent a Set-Cookie header or a Cache-Control of private, no-store or max-age=0.

Two settings get you from the default to a proper static CDN:

  1. A cache rule for /static/ and /media/ that sets the edge TTL and browser TTL to a year. Magento's versioned /static/ URLs change on every deploy, so long TTLs are safe there. For /media/, a product image that is replaced keeps its filename, so purge that URL when you replace it, or let a module do it.
  2. Make sure the origin does not set cookies on static responses. A stray PHPSESSID on a CSS file turns every request into DYNAMIC. It happens when /static/ and /media/ requests fall through to Magento's PHP entry points instead of being served directly by nginx; the stock nginx.conf.sample handles this correctly.

You do not need a separate CDN hostname. Magento's "Base URL for Static View Files" and "Base URL for User Media Files" settings exist for CDNs that pull from a different origin, and most guides have you fill them in. With Cloudflare proxying the main hostname, leave them alone. One hostname, one certificate, one cache.

What the free plan will not do for static files is re-encode images. Polish, which strips metadata and recompresses images at the edge, starts on the Pro plan. Whether that is worth the upgrade depends on how much image weight your theme ships and whether you already generate WebP on the server.

HTML: the part that moves the needle

Here is the thing a page speed report will not tell you. Once static files are on the edge, the slowest request on a product page is the HTML document itself. Time to first byte on that request is Magento's full page cache doing its job, or not.

Magento ships two full page cache backends. The built-in one stores rendered pages in the application cache. Varnish sits in front of PHP and serves them without touching Magento at all. Adobe's documentation recommends Varnish for production. Either way, the cache lives on your server, in one region, and every anonymous page view still crosses the internet to reach it.

Cloudflare does not cache HTML by default. Its documentation is explicit: HTML and JSON are not cached unless a rule or the origin's headers say otherwise. So a store with the orange cloud on and a perfect Varnish setup still shows cf-cache-status: DYNAMIC on every category page, and every visitor in Sydney waits for a round trip to Virginia.

The tempting fix is a cache rule that caches everything on the storefront hostname. Do not do that. Magento sets session and context cookies on precisely the pages that must never be shared: the cart, checkout, customer account, and any product page viewed by a logged-in customer with group pricing. A blanket cache rule ignores those signals and serves the first shopper's cart to the second.

The correct approach is to let Magento decide. Magento's full page cache already knows which pages are safe to store: every block cacheable, the request is a GET, no bypass condition, no context cookie set. That decision can be sent to Cloudflare as headers (Cache-Control: public, s-maxage=<ttl> plus a Cache-Tag header built from Magento's own cache tags) on exactly the pages the full page cache would store, with private, no-store on everything else. Then Cloudflare caches the same set of pages Varnish would, and a product save purges the edge by tag the same way it cleans the full page cache.

That is what OCM Labs Cloudflare does, and it is free and open source, so this is not a sales pitch so much as a description of the only safe way to do it. The launch post walks through the mechanics in detail. If you would rather build it yourself, the design brief is: emit the headers from a plugin on the page cache kernel, strip the session cookies from cacheable responses the way Magento's Kernel and VCL already do, bypass the X-Magento-Vary cookie, and purge by tag through a queue, because Cloudflare's purge API has rate limits and a catalog import will hit them.

Expected result: anonymous visitors on the default store view get the category and product HTML from the edge. Logged-in customers and anyone on a non-default store view or currency bypass the edge and are served by your full page cache, which is still fast. On most stores the anonymous default-context visitor is the majority, so the edge takes most of the HTML load off the origin.

Security beyond the cache

A Magento store is a login form, a checkout and an admin panel, and all three get automated traffic every day. This is where Cloudflare earns its place for stores that never turn on HTML caching at all.

Lock the admin behind something other than a password. Put the admin path behind Cloudflare Access, so a Cloudflare login (email one-time code, Google, Okta or whatever you use) is required before the request reaches Magento's admin login at all. Cloudflare Access has a free tier for small teams. If that is more than you want, a WAF custom rule that blocks the admin path for every IP except your office and your deployment runner does most of the job.

Rate limit the login endpoints. customer/account/loginPost, the admin login and the REST token endpoint are the three that credential stuffing hits. One rule on the free plan covers the customer login; the paid plans add rules and longer windows.

Turn on the managed WAF rules. The Free Managed Ruleset is on by default. On Pro, enable the Cloudflare Managed Ruleset and the OWASP Core Ruleset and run them in log mode for a week before switching to block, because an OWASP score threshold set too low will block legitimate admin saves that contain HTML.

Challenge the bots that are not search engines. Bot Fight Mode on the free plan is blunt: it challenges anything matching bot patterns and cannot be scoped. If you run a mobile app or a headless frontend against the REST or GraphQL API, it will challenge those too, and Cloudflare says so in its documentation. Super Bot Fight Mode on Pro and Business can be configured per category of bot and supports skip rules for your own API paths. Watch the firewall events for a few days before you trust either.

Close the back door. The IP allowlist and Authenticated Origin Pulls from the first section are what make all of the above binding. A WAF that can be walked around by connecting to the origin directly is not a WAF.

Reliability, and one feature to turn off

Tiered Cache and edge caching together mean that a traffic spike, a sale email going out or a product going viral, is mostly answered by Cloudflare. The origin sees cache fills and the bypass traffic (cart, checkout, logged-in visitors), which is the traffic that was always going to need PHP.

Always Online is the feature to turn off. When Cloudflare cannot reach the origin, it serves a copy of the page from the Internet Archive, with a banner. For a blog, fine. For a store, that is yesterday's prices and stock served to a shopper who then cannot add anything to a cart. Cloudflare's own documentation notes that dynamic features such as shopping carts cannot work in that state. Let the store show an error page you control instead.

One more operational detail: on plans below Enterprise, Cloudflare waits 100 seconds for the origin to respond before returning a 524 error. Most storefront requests are nowhere near that. Long admin actions (a large export, a reindex triggered from the admin, a product import of several thousand rows) can be, and the browser will see a 524 while the PHP process keeps running on the server. Move those jobs to cron or the CLI, or exempt the admin hostname from the proxy if you have a separate one.

What breaks, and the fix for each

These are the settings that cause the "we put Cloudflare in front of Magento and now checkout is broken" ticket. All of them are a single toggle.

WHAT IT DOES TO MAGENTOFIX
SSL MODE: FLEXIBLECloudflare talks plain HTTP to the origin, Magento's secure base URL redirects to HTTPS, Cloudflare arrives over HTTP again. Infinite redirect loop.Set SSL mode to Full (strict) with a valid origin certificate; a free Cloudflare Origin CA certificate works.
ROCKET LOADERRewrites how scripts load. Breaks RequireJS on Luma and Alpine.js on Hyva.Off, under Speed > Optimization > Content Optimization.
AUTO MINIFY (OLDER ZONES)Can break RequireJS module definitions and Alpine expressions. Magento minifies its own assets.Off.
EMAIL OBFUSCATIONRewrites email addresses in rendered HTML and injects a script into cached pages.Off, under Scrape Shield.
BROWSER CACHE TTL OVERRIDEReplaces the origin's max-age on HTML, so a purged page stays in visitors' browsers until the override expires.Respect Existing Headers.
CACHE EVERYTHING RULECaches cart, checkout and logged-in pages for the next visitor.Delete it. Cache HTML by origin headers only.
BOT FIGHT MODE WITH AN API CLIENTChallenges REST and GraphQL requests from your app or integration.Super Bot Fight Mode with a skip rule, or an IP Access rule for the integration.

Two more that are not Cloudflare settings but show up in the same ticket:

  • The admin cookie path. If you add the admin to Bypass Paths or exclude it from the proxy, keep the admin on the same hostname and scheme, or Magento's admin session cookie will not match.
  • Payment callbacks. PayPal, Stripe, Adyen and every other gateway POST back to your store. POSTs are never cached, but a WAF rule or Bot Fight Mode can block the callback. Add the callback paths to a skip rule and watch the firewall log after the first live order.

A go-live checklist

Roughly in order, for a store moving to Cloudflare for the first time:

  1. Fix the real visitor IP at the web server before you flip DNS, so the first order after cutover has the right address on it.
  2. Install an Origin CA certificate on the origin and set SSL mode to Full (strict).
  3. Restrict the origin to Cloudflare's IP ranges and enable Authenticated Origin Pulls.
  4. Flip the storefront DNS record to proxied. Check cf-cache-status on a CSS file and on a category page: HIT and DYNAMIC respectively is the expected starting state.
  5. Turn off Rocket Loader, Auto Minify, Email Obfuscation and Always Online. Set Browser Cache TTL to Respect Existing Headers.
  6. Add the cache rule for /static/ and /media/.
  7. Add a rate limit on the customer login path, and either Access or an IP rule on the admin path.
  8. Place a test order through every payment method, then log in as a customer and confirm the cart and account pages show the right person.
  9. Only then, decide about HTML caching. Turn it on with something that follows Magento's full page cache decision, run the health checks it gives you, and check cf-cache-status on a category page again. It should now say HIT on the second request, and the login page should still say BYPASS or DYNAMIC.

If you want a second pair of eyes on a Cloudflare cutover, or a store that is behind Cloudflare and still slow, performance and infrastructure is the work we do most weeks.

FAQ

Is Cloudflare a good CDN for Magento?+
Yes, on two conditions: the five settings in the table above are off, and HTML caching is either left off or driven by Magento's own cache headers. With those in place it is the cheapest way to get static files on the edge, hide the origin and put DDoS mitigation and a WAF in front of the store.
Does the free Cloudflare plan work for a Magento store?+
The free plan covers the orange-cloud proxy, unmetered DDoS protection, the Free Managed WAF Ruleset, Bot Fight Mode, one rate limiting rule, Tiered Cache, HTTP/3, Brotli, and purge by cache tag. Pro adds the full managed WAF rulesets, Super Bot Fight Mode, image Polish and more purge headroom. Enterprise adds custom cache keys, which is what you need to edge-cache non-default store views and currencies.
Does Cloudflare replace Varnish for Magento?+
No. Varnish, or the built-in full page cache, still serves every request Cloudflare bypasses: logged-in customers, non-default store views, and every cache fill. Cloudflare sits in front of it. Behind Varnish, the stock Magento VCL strips the cache headers before they leave the server, so you need a Cloudflare-aware VCL for the edge to cache anything.
Why does Magento show a redirect loop behind Cloudflare?+
Almost always SSL mode set to Flexible. Cloudflare connects to the origin over HTTP, Magento redirects to its secure base URL, and the cycle repeats. Set SSL mode to Full (strict) and give the origin a certificate.
Why does cf-cache-status say DYNAMIC on my category pages?+
Because Cloudflare does not cache HTML unless the origin's headers or a cache rule tell it to, and Magento's stock headers do not. Either leave it that way, which is safe, or add headers that follow Magento's full page cache decision. Do not fix it with a Cache Everything rule.
Should I put the Magento admin behind Cloudflare?+
Keep it proxied, so it gets the same DDoS protection and WAF, and add Access or an IP rule in front of the admin path. Move long-running admin jobs to cron or the CLI so they do not hit the 100 second origin timeout.
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