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.

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=preloadheaders 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-statusHIT 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:
- 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. - Make sure the origin does not set cookies on static responses. A stray
PHPSESSIDon a CSS file turns every request intoDYNAMIC. It happens when/static/and/media/requests fall through to Magento's PHP entry points instead of being served directly by nginx; the stocknginx.conf.samplehandles 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 MAGENTO | FIX | |
|---|---|---|
| SSL MODE: FLEXIBLE | Cloudflare 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 LOADER | Rewrites 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 OBFUSCATION | Rewrites email addresses in rendered HTML and injects a script into cached pages. | Off, under Scrape Shield. |
| BROWSER CACHE TTL OVERRIDE | Replaces the origin's max-age on HTML, so a purged page stays in visitors' browsers until the override expires. | Respect Existing Headers. |
| CACHE EVERYTHING RULE | Caches cart, checkout and logged-in pages for the next visitor. | Delete it. Cache HTML by origin headers only. |
| BOT FIGHT MODE WITH AN API CLIENT | Challenges 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:
- 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.
- Install an Origin CA certificate on the origin and set SSL mode to Full (strict).
- Restrict the origin to Cloudflare's IP ranges and enable Authenticated Origin Pulls.
- Flip the storefront DNS record to proxied. Check
cf-cache-statuson a CSS file and on a category page:HITandDYNAMICrespectively is the expected starting state. - Turn off Rocket Loader, Auto Minify, Email Obfuscation and Always Online. Set Browser Cache TTL to Respect Existing Headers.
- Add the cache rule for
/static/and/media/. - Add a rate limit on the customer login path, and either Access or an IP rule on the admin path.
- 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.
- 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-statuson a category page again. It should now sayHITon the second request, and the login page should still sayBYPASSorDYNAMIC.
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?+–
Does the free Cloudflare plan work for a Magento store?+–
Does Cloudflare replace Varnish for Magento?+–
Why does Magento show a redirect loop behind Cloudflare?+–
Why does cf-cache-status say DYNAMIC on my category pages?+–
Should I put the Magento admin behind Cloudflare?+–

Shane Blandford
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.
✦ KEEP READING
Related from the Journal.
OCM Labs Cloudflare: free edge caching for Magento, on Magento's own terms
A free, open-source Magento module that caches storefront HTML at the Cloudflare edge using Magento's own full page cache rules, and purges by cache tag.

How to use Cloudflare development mode and analytics from the Magento admin
Turn Cloudflare development mode on before a theme deploy, run a quick purge and read Cloudflare analytics from the Magento admin with a free module.

How to get the real visitor IP in Magento behind Cloudflare
Every Magento order shows a Cloudflare IP? Restore the real visitor address from CF-Connecting-IP safely, fix load balancers, and verify it.
