CDN Setup and Configuration

Keyboard shortcuts
  • JNext lesson
  • KPrevious lesson
  • /Search lessons
  • EscClear search

Your WordPress server sits in one location. Maybe Dallas, maybe Amsterdam, maybe Singapore. When someone from the other side of the world visits your site, their request travels thousands of miles to your server, gets processed, and the response travels thousands of miles back. Physics is involved. Light through fiber optic cables is fast, but it’s not instant. That round trip adds 200-500ms of latency depending on distance.

A CDN fixes this. And for most WordPress sites, it’s either free or costs $5/month.

I’ve set up CDNs on 200+ client sites. The pattern is the same every time: 10 minutes of setup, and visitors from far away suddenly load the site 30-50% faster. Close visitors see improvement too, because CDNs also cache and optimize your content. It’s one of the best return-on-time investments in WordPress performance.

What a CDN Actually Does

CDN stands for Content Delivery Network. It’s a network of servers spread across the world. When you use a CDN, copies of your site’s files (images, CSS, JavaScript, sometimes even full pages) get stored on servers in dozens of locations. When someone visits your site, they’re served from the server closest to them.

A visitor in Tokyo doesn’t wait for a response from your server in Texas. They get served from a CDN server in Tokyo. A visitor in London gets served from a London server. Same content, much shorter distance.

The speed improvement comes from two things:

Reduced latency. The physical distance between the visitor and the server shrinks. A request that used to travel 12,000 miles now travels 200 miles. That saves 100-400ms per request.

Reduced server load. Your origin server doesn’t handle every request. The CDN absorbs most of the traffic for static files. Your server focuses on dynamic content (processing PHP, running database queries). This means it responds faster to the requests it does handle.

For static sites, a CDN is almost like magic. For WordPress sites with dynamic content, the benefit is mostly on static assets (images, CSS, JS, fonts) unless you use a full-page caching CDN like Cloudflare APO.

Why Every Site Needs a CDN

I hear this a lot: “My site is small. I don’t need a CDN.” Wrong.

Even if your audience is mostly local, a CDN still helps. Cloudflare’s free tier includes:

DDoS protection. Your site doesn’t go down when someone attacks it. I’ve had client sites survive DDoS attacks that would have taken down the origin server.

SSL/TLS. Free HTTPS for your site. Cloudflare handles the certificate.

Caching of static assets. Your server handles fewer requests, so it’s faster for everything.

DNS resolution. Cloudflare’s DNS is one of the fastest in the world. That shaves 20-50ms off every first visit compared to many registrar DNS servers.

Web Application Firewall (WAF). Basic protection against common attacks, included free.

All of this. For free. There’s no reason not to use it. I put Cloudflare on every single site I build, even a local restaurant site with 50 visitors a day. The security and performance benefits justify the 10 minutes of setup.

Cloudflare Free Tier Setup

Cloudflare is the CDN I recommend for every WordPress site. The free tier is genuinely useful, not a watered-down trial. Here’s the setup process.

Step 1: Create a Cloudflare Account

Go to cloudflare.com and sign up. Free.

Step 2: Add Your Site

Click “Add a Site” and enter your domain. Cloudflare scans your existing DNS records. This takes about 60 seconds.

Step 3: Select the Free Plan

Cloudflare shows you plan options. Select Free. You can upgrade later if needed.

Step 4: Verify DNS Records

Cloudflare shows you the DNS records it found. Verify they match your current records. The important ones:

A record pointing to your server’s IP address. This should already be there.

CNAME records for www or subdomains. Verify these are correct.

MX records for email. Make sure these are present. Missing MX records means your email stops working.

Each record has a proxy status toggle (the orange cloud icon). Records with the orange cloud route through Cloudflare (CDN, security, caching). Records with the grey cloud bypass Cloudflare (direct to server). Set your A and CNAME records to proxied (orange cloud). Set your MX records to DNS only (grey cloud). Email should never route through Cloudflare.

Step 5: Change Your Nameservers

Cloudflare gives you two nameservers. Go to your domain registrar (where you bought the domain) and replace the existing nameservers with Cloudflare’s. This is the only step that takes time. DNS propagation means it can take 1-48 hours for the change to fully take effect. Usually it’s done within 2-4 hours.

Step 6: Configure SSL

In Cloudflare’s SSL/TLS settings, set the encryption mode to “Full (Strict)” if your origin server already has an SSL certificate (most hosts provide free Let’s Encrypt certificates). This encrypts the connection between Cloudflare and your server. If you set it to “Flexible,” the connection between Cloudflare and your server is unencrypted, which is a security problem.

That’s it. Your site now runs through Cloudflare.

Cloudflare Settings That Actually Matter

Cloudflare has dozens of settings. Most of them you can leave at defaults. These are the ones worth changing.

Caching Level

Go to Caching > Configuration. Set Caching Level to “Standard.” This caches static files based on the file extension. CSS, JS, images, fonts, all cached. HTML is not cached by default (you need APO for that, covered below).

Browser TTL

Set Browser Cache TTL to “Respect Existing Headers.” Your caching plugin (FlyingPress, WP Rocket) already sets proper cache-control headers for your assets. Let Cloudflare respect those instead of overriding them. If you set a specific TTL here, it overrides what your server sends, which can cause conflicts.

Polish (Image Optimization)

Polish compresses images on the fly as Cloudflare serves them. It’s a Pro plan feature ($20/month). If you’re already optimizing images with ShortPixel or Imagify (which you should be), Polish is redundant. Skip the upgrade just for this.

Rocket Loader

Cloudflare’s Rocket Loader defers JavaScript loading. Sounds good in theory. In practice, it breaks things on WordPress sites constantly. It conflicts with caching plugins, breaks inline scripts, and causes random JavaScript errors.

Turn it off. Your caching plugin handles JavaScript deferral better.

Minification

Cloudflare offers auto-minification for CSS, JS, and HTML. If your caching plugin already minifies (FlyingPress and WP Rocket both do), disable Cloudflare’s minification. Double minification can cause issues and slows down Cloudflare’s processing.

If you’re not using a caching plugin with minification, enable it in Cloudflare. But you should be using a caching plugin.

Speed > Optimization

Cloudflare has an “Early Hints” feature. Enable it. It tells the browser to preload resources before the HTML even finishes loading. It’s a small optimization, but it’s free and doesn’t break anything.

Cloudflare APO: The $5 Game Change

Regular Cloudflare caches static files (CSS, JS, images) but doesn’t cache your HTML pages. Your origin server still processes every page request. For WordPress, this means the CDN helps with assets but doesn’t help with your slowest component: page generation.

Cloudflare APO (Automatic Platform Optimization) for WordPress changes this. For $5/month, it caches your full HTML pages on Cloudflare’s edge servers. That means visitors anywhere in the world get served a fully cached HTML page from a server near them. Your origin server barely gets touched.

The results are significant. I’ve seen TTFB drop from 500-800ms to 30-80ms for international visitors after enabling APO. That’s not a minor improvement. That’s the difference between a fast site and a slow site.

Setting Up APO

  1. In Cloudflare dashboard, go to Speed > Optimization > APO
  2. Enable APO for WordPress ($5/month on the free plan, included with Pro plan)
  3. Install the “Cloudflare” WordPress plugin
  4. Go to Settings > Cloudflare in your WordPress admin
  5. Enter your Cloudflare API credentials
  6. Enable APO in the plugin settings

The WordPress plugin communicates with Cloudflare to automatically purge cached pages when you publish or update content. This is important. Without it, visitors might see stale content after you make changes.

APO vs. Your Caching Plugin

APO doesn’t replace your caching plugin. It adds a layer on top. Your caching plugin handles page caching at the server level. APO caches at the CDN level. Both work together.

The flow: visitor requests a page. Cloudflare checks if it has a cached copy. If yes, it serves it (fastest possible). If no, the request goes to your server, where your caching plugin serves a cached copy (fast, but not as fast as edge caching). If neither has a cache, WordPress generates the page from scratch.

Keep your caching plugin active. APO handles the global distribution. Your caching plugin handles the origin caching.

Alternative CDNs

Cloudflare is my default recommendation, but there are alternatives worth knowing about.

Bunny CDN

If you want more control over your CDN configuration, Bunny CDN is solid. It’s a pull CDN (meaning it automatically pulls content from your server when requested) with straightforward pricing: about $0.01 per GB of bandwidth. For most WordPress blogs, that’s $1-5/month.

Bunny CDN gives you more granular cache rules, custom SSL certificates, and detailed analytics. I use it for sites that need specific cache behaviors that Cloudflare doesn’t offer on the free plan.

Setup is different from Cloudflare. Instead of changing nameservers, you create a “pull zone” with Bunny CDN and point it to your origin server. Then you configure your WordPress site to serve static assets from the Bunny CDN URL. FlyingPress has a built-in CDN integration for this.

KeyCDN

Similar to Bunny CDN. Pull-based, pay-per-usage pricing. Good performance, less feature-rich than Bunny. I haven’t used it in a few years, so I can’t speak to its current state with confidence.

When to Use Something Other Than Cloudflare

Cloudflare works for 90% of WordPress sites. Consider alternatives when:

You need precise cache control. Cloudflare’s free and Pro plans have limited cache rules. Bunny CDN gives you more flexibility.

Cloudflare is blocked in your target market. In some countries, Cloudflare’s IP ranges are occasionally throttled. If your primary audience is in one of these regions, test your site speed with and without Cloudflare.

You want to avoid changing nameservers. Cloudflare requires nameserver changes. Bunny CDN and KeyCDN work as standard CDNs without touching your DNS management.

For most readers of this course? Cloudflare free tier plus APO. $5/month. Done.

CDN for Media: Offloading Images and Videos

By default, your images and videos are stored on your WordPress server and served from your domain. With a CDN, these files are cached at edge servers, but they still originate from your server.

For sites with a lot of media (photography blogs, WooCommerce stores with hundreds of product images), offloading media to a dedicated CDN or cloud storage can reduce server load and improve delivery speed.

Image CDN Options

Cloudflare with APO: Already caches your images at the edge. For most sites, this is enough.

Bunny CDN with a CDN subdomain: Set up a subdomain like cdn.yoursite.com that points to Bunny CDN. Use a plugin like “WP Offload Media” to rewrite your image URLs to the CDN subdomain. Images are served from Bunny’s global network.

Cloudflare R2 + Workers: For advanced setups. Store images in Cloudflare R2 (object storage) and serve them via Cloudflare Workers. No egress fees, global delivery. Overkill for most blogs, but useful for media-heavy sites.

Video

Never host video files on your WordPress server. Ever. A single 5-minute 1080p video is 50-200MB. Your server isn’t designed for video delivery.

Use YouTube, Vimeo, or Bunny Stream for video hosting. Embed them on your WordPress pages. If you’re concerned about YouTube’s performance impact (it loads 1MB+ of resources per embed), use a lazy-load facade. Plugins like “WP YouTube Lyte” or FlyingPress’s built-in YouTube lazy loading replace the embed with a lightweight thumbnail. The actual YouTube player only loads when the visitor clicks play.

DNS Propagation: What to Expect

When you switch to Cloudflare (or any new DNS provider), your nameserver change needs to propagate across the internet. This isn’t instant.

What happens: DNS servers worldwide update their records to point your domain to Cloudflare’s nameservers. Some update in minutes. Others take hours. A few stubborn ones take up to 48 hours.

What you’ll experience: During propagation, some visitors hit your old server directly. Others hit Cloudflare. This is normal. Your site works either way, it’s just that the CDN benefits aren’t consistent for everyone until propagation completes.

How to check: Use whatsmydns.net. Enter your domain and select “NS” record type. It shows you which nameservers are resolving for your domain across dozens of global DNS servers. When they all show Cloudflare’s nameservers, propagation is complete.

Don’t panic during propagation. Your site won’t go down (assuming you verified DNS records correctly in Cloudflare). It just might be slower for some visitors until the CDN kicks in fully.

Pro tip: Lower your DNS TTL (Time to Live) at your current registrar 24-48 hours before switching to Cloudflare. Set it to 300 seconds (5 minutes). This tells DNS servers to check for updates more frequently, which speeds up propagation. After the switch is complete, Cloudflare manages TTL automatically.

CDN Debugging: Verifying Your CDN Works

After setup, you need to verify that Cloudflare is actually serving your content. Trust but verify.

Check Response Headers

Open Chrome DevTools > Network tab. Click on any request. Look for these headers:

cf-cache-status. This tells you if Cloudflare cached the response.

  • HIT = Served from Cloudflare’s cache. Fast.
  • MISS = Cloudflare didn’t have a cached copy. Fetched from your origin server.
  • DYNAMIC = The resource is not eligible for caching (usually HTML without APO).
  • EXPIRED = The cached copy expired and was refreshed from origin.
  • BYPASS = Cloudflare was told not to cache this (by a page rule or cookie).

For static assets (CSS, JS, images), you should see HIT after the first request. For HTML pages with APO enabled, you should also see HIT.

cf-ray. This header includes a code indicating which Cloudflare data center served the request. The last three letters are the airport code of the data center. If you’re in New York and you see “EWR” (Newark) or “JFK,” Cloudflare is routing correctly. If you see a far-away code, there might be a routing issue.

server. Should show “cloudflare” if the request went through Cloudflare.

When You See MISS

A MISS on the first request is normal. Cloudflare caches content after the first request. Reload the page. The second request should be HIT.

If you consistently see MISS:

  1. Check that Cloudflare’s proxy is enabled (orange cloud in DNS settings)
  2. Verify the caching level isn’t set to “No Query String” (use “Standard”)
  3. Check if a page rule or Transform Rule is bypassing cache
  4. Make sure your origin server isn’t sending Cache-Control: no-cache headers for static files

Common CDN Mistakes

I’ve debugged enough CDN setups to know the common failure modes.

Caching Dynamic Pages Without APO

If you set up a Cloudflare page rule to cache everything (Cache Level: Cache Everything), you’ll cache logged-in user sessions, admin pages, and WooCommerce carts. Visitors will see each other’s shopping carts. Login forms will break.

Don’t create broad caching rules manually. Use APO instead. It knows how to handle WordPress’s dynamic content correctly.

Breaking Forms

If contact form submissions stop working after enabling Cloudflare, the problem is usually the Rocket Loader or the WAF blocking POST requests. Disable Rocket Loader (you should already have it off). Check the Cloudflare Firewall Events log for blocked requests. Add the form submission URL to the Firewall allow rules if needed.

Login Issues

After enabling Cloudflare, some sites report issues with WordPress login. The login page gets cached, or the Cloudflare challenge page appears for your own admin access.

Fix: In Cloudflare’s page rules, create a rule for yoursite.com/wp-admin/* and yoursite.com/wp-login.php with cache level set to “Bypass.” This ensures your admin area never gets cached and doesn’t receive Cloudflare security challenges. Most caching plugins and APO handle this automatically, but manual rules are a safety net.

Mixed Content Warnings

If your site shows mixed content warnings after enabling Cloudflare’s SSL, it means some resources (images, scripts) are loading over HTTP while the page is HTTPS. Use the “Really Simple SSL” plugin to fix mixed content, or update the URLs in your database using WP-CLI:

wp search-replace 'http://yoursite.com' 'https://yoursite.com' --all-tables

Run this once, and the mixed content warnings disappear.


Chapter Checklist

  • [ ] Cloudflare account created and site added
  • [ ] DNS records verified (A, CNAME proxied; MX records DNS-only)
  • [ ] Nameservers changed at domain registrar
  • [ ] SSL set to “Full (Strict)” in Cloudflare
  • [ ] Rocket Loader disabled
  • [ ] Cloudflare APO enabled ($5/month) with WordPress plugin installed
  • [ ] Browser TTL set to “Respect Existing Headers”
  • [ ] Response headers verified (cf-cache-status: HIT for static assets)
  • [ ] APO verified (cf-cache-status: HIT for HTML pages)
  • [ ] wp-admin and wp-login.php bypassing cache
  • [ ] Video content hosted externally (YouTube/Vimeo) with lazy-load facades
  • [ ] DNS propagation confirmed via whatsmydns.net

Chapter Exercise

Set up Cloudflare on your site (if you haven’t already) and verify it works:

  1. Sign up at cloudflare.com and add your domain
  2. Verify all DNS records, especially MX records for email
  3. Change nameservers at your registrar
  4. Wait for propagation (check whatsmydns.net)
  5. Enable APO and install the Cloudflare WordPress plugin
  6. Disable Rocket Loader and auto-minification (your caching plugin handles these)
  7. Visit your site in an incognito window with DevTools open
  8. Check cf-cache-status headers on a CSS file (should be HIT on second load)
  9. Check cf-cache-status on your HTML page (should be HIT with APO)
  10. Log into WordPress and verify the admin works normally

Record your TTFB before and after Cloudflare. Test from a distant location using WebPageTest.org (pick a test location far from your server). You should see a significant TTFB reduction for the distant test.

Disclaimer: This site is reader-supported. If you buy through some links, I may earn a small commission at no extra cost to you. I only recommend tools I trust and would use myself. Your support helps keep gauravtiwari.org free and focused on real-world advice. Thanks. - Gaurav Tiwari