Learn / Setting Up a CDN Without Breaking Anything

Setting Up a CDN Without Breaking Anything

GUIDE

7 min read · 1,461 words

Adding a CDN is quick. Adding one correctly takes an afternoon. This guide is for the afternoon.

Adding a CDN takes about ten minutes: sign up, change a DNS record, watch the site carry on working. Adding one correctly takes an afternoon, because a CDN is a cache in front of your site, and a cache that does not know which pages are personal will cheerfully show one visitor's basket to the next.

A content delivery network keeps copies of your files on servers in many locations and answers visitors from whichever is nearest. That cuts the distance data travels, and it takes a good share of requests away from your own server. Done well, the site gets faster and your hosting account gets quieter. Done carelessly, you get redirect loops, stale pages, logged-in users seeing public content, and logs full of addresses that belong to the CDN rather than your visitors.

This guide is the afternoon version. It is about the order of operations: decide what can be cached, connect the CDN, set the encryption mode, write the rules, and test the awkward cases before you tell anyone it is live.

Decide what is static and what is personal

Before you touch any settings, list the site's content in two columns. Static things are the same for every visitor: images, stylesheets, JavaScript files, fonts, downloadable PDFs, and, on a brochure site, the HTML pages themselves. Personal things differ per visitor or change constantly: the login page, the admin area, the cart and checkout, "my account", anything behind a session cookie, search results, and API calls that return user data.

Static files are safe to cache widely and for a long time. Personal responses must bypass the cache entirely. The hard cases sit in between: a blog page that is identical for everyone except for a small "logged in as" bar, or a product page that shows stock levels. You can cache those if you can reliably recognise a logged-in request (usually by a cookie name) and send those requests past the cache.

If you only remember one rule: caching images, CSS and JavaScript is nearly always right, and caching HTML is a decision you make deliberately, page type by page type.

Two ways to connect

A pull zone serves your static files from a separate hostname such as cdn.example.com. The CDN fetches each file from your origin the first time it is asked for, keeps it, and serves later requests itself. Your pages stay on www.example.com; you (or a plugin) rewrite asset URLs so images and scripts point at the CDN hostname. It is low risk, because HTML never passes through the CDN, and a mistake cannot expose private pages.

A reverse-proxy CDN sits in front of the whole hostname. You change your nameservers or point a CNAME at the provider, and every request, HTML included, goes to the CDN first. It can cache what you allow, filter abusive traffic and terminate TLS. It also means the CDN is now a part of everything, including login pages, so the rules matter far more.

Visitor browser CDN edge nearest location Origin your hosting 1. request 3. miss: fetch 2. hit: answered here 4. response stored at the edge, then sent to the visitor
A cache hit never reaches your server; a miss goes to the origin once and is then stored.

Set the encryption mode correctly

With a reverse proxy there are two separate connections: visitor to CDN, and CDN to origin. Each has its own encryption setting. The visitor side uses the certificate the CDN issues. For the origin side, install a valid certificate on your server (a free ACME one is fine) and set the CDN to verify it. Providers name this differently, but it is often called "Full (strict)".

The setting to avoid is the one that connects to your origin over plain HTTP while showing visitors a padlock. It looks fine in the browser and fails in two ways. First, if your origin redirects HTTP to HTTPS, which it should, the CDN asks over HTTP, receives a redirect, asks again, and the browser eventually reports "too many redirects". Second, traffic between the CDN and your server travels unencrypted.

Plain HTTP to origin CDN Origin GET over http, answer: 301 to https, repeat Result: redirect loop HTTPS, certificate verified CDN Origin GET over https, answer: 200 page
Why the plain-HTTP origin mode loops when the origin also forces HTTPS.

Write the cache rules

Start conservative and widen later. The usual order is: static assets cached for a long time, everything personal bypassed, HTML cached only if you can prove you are excluding the right requests.

Long caching for static files only works safely if the filenames change when the content does. WordPress adds ?ver=1.2.3 to many assets, which helps; build tools often add a hash such as app.3f9a1c.js, which is better. On your origin you can send the instruction yourself:

<FilesMatch "\.(css|js|woff2|webp|avif|jpe?g|png|svg)$">
  Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>

For HTML, write the rules as bypass conditions first. Bypass any path starting /wp-admin, /wp-login.php, /cart, /checkout and /my-account; bypass any request carrying a cookie whose name starts with wordpress_logged_in_, woocommerce_ or your application's session cookie. Then, and only then, allow HTML caching for everything else with a modest lifetime of a few minutes to an hour.

Rule order matters. Most CDNs apply the first matching rule, so put the bypass rules above the "cache everything" rule. A cache-everything rule placed first is how private pages end up cached.

Test the awkward cases

  1. Load the site in a private window and look at the response headers: curl -sI https://www.example.com/wp-content/uploads/photo.jpg. Look for a cache status header (the name varies by provider, x-cache or cf-cache-status are common). The first request usually says MISS, the second HIT.
  2. Log in, then browse the public pages. You should see your admin bar and your own name, never a copy cached for logged-out visitors. Log out and check the reverse.
  3. Add an item to a basket in one browser, then open the shop in a second browser with no cookies. It should show an empty basket.
  4. Edit a page and publish it. Check how long the old version lingers, then purge that URL from the CDN and confirm the new one appears.
  5. Submit a contact form and confirm it is delivered. Forms that rely on a nonce embedded in cached HTML can fail once the nonce expires.
  6. Check https://www.example.com/robots.txt, /sitemap.xml and /feed/. Stale copies of these confuse search engines.

Real visitor addresses in your logs

With a reverse proxy, every request reaches your server from a CDN address, so your access log suddenly shows a few dozen addresses instead of your visitors. Rate limiting, security plugins and analytics that rely on the connecting address then treat thousands of people as one, and may block the lot.

The CDN passes the real address in a header, commonly X-Forwarded-For or a provider-specific one. Tell your server to trust that header only from the CDN's published ranges. On Apache with mod_remoteip:

RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 198.51.100.0/24

Use the real ranges from your provider's documentation; the address above is an example. Trusting the header from everyone lets anyone forge their address, so keep that list tight. On shared hosting you may need to ask support whether this is already configured.

What changes by hosting type

On shared hosting you usually cannot edit server configuration, so you lean on the CDN's dashboard, .htaccess headers and a plugin. On a VPS or dedicated server you control the origin and can lock it down so that only the CDN's address ranges may connect to port 443, which stops anyone bypassing the CDN by finding your real IP. On managed WordPress hosting there is often a built-in CDN or page cache already, and adding a second layer on top can double-cache pages and make purging confusing. Check what is already there before adding more.

Keep an eye on it

Look at the cache hit ratio in the CDN dashboard. For a mostly static site a healthy figure is high; for a shop with heavy personal traffic, a lower one is expected. A very low ratio on static files usually means something is sending Cache-Control: no-store, or query strings are making every URL look unique. Watch your origin's traffic too: it should fall after the switch. If it does not, the CDN is not doing its job. Finally, put the certificate and the CDN's account renewal on your calendar. A lapsed CDN subscription is an outage you cannot fix from your own server. The page weight tool helps you see which assets are worth caching first.

Checklist