Building a Mini Private CDN for WordPress: High-Performance Caching with Caddy and Varnish on Two VPS
Introduction: Why Build a Private Mini-CDN for WordPress?
In the modern digital economy, website performance is directly correlated with user retention, conversion rates, and Search Engine Optimization (SEO) rankings. While commercial Content Delivery Networks (CDNs) like Cloudflare, KeyCDN, or AWS CloudFront offer robust global edge caching, they can introduce certain limitations. Enterprise-level customization often carries a premium price tag, and privacy-conscious organizations may hesitate to route all unencrypted traffic through third-party infrastructure.
For businesses seeking ultimate control over their data routing, custom caching logic, and infrastructure costs, building a private mini-CDN is an exceptionally viable alternative. By leveraging the modern efficiency of Caddy Server and the industry-standard caching capabilities of Varnish Cache across two separate Virtual Private Servers (VPS), you can dramatically reduce Time to First Byte (TTFB) and ensure your WordPress site handles traffic spikes seamlessly.
The Architecture Overview: How It Works
A typical mini-CDN architecture separates the content generation layer from the content delivery layer. In our two-VPS topology, the infrastructure is divided as follows:
- VPS 1 (The Origin Server): This server hosts your core WordPress installation, database (MySQL/MariaDB), and PHP-FPM processor. It handles all dynamic administrative tasks (such as WP-Admin) and generates the raw HTML pages.
- VPS 2 (The Edge Server / Private CDN node): Strategically located closer to your primary target audience, this server runs Varnish Cache and Caddy Server. It intercepts all incoming public requests, serving cached static assets and HTML instantly without querying the origin server.
When a user requests a page, Caddy acts as the secure reverse proxy, handling SSL/TLS termination automatically. It passes the request to Varnish Cache. If Varnish has the page in its memory (a cache hit), it returns it immediately. If not (a cache miss), Varnish fetches the content from VPS 1, saves a copy for future visitors, and delivers it to the user.
Step 1: Preparing the Origin Server (VPS 1)
Before establishing our edge node, your origin server must be securely configured to accept traffic exclusively from your edge node, protecting it from direct-to-IP bypass attacks.
1. Configure WordPress Permissions
Ensure your WordPress site operates under a standard environment (Nginx/Apache + PHP). However, because the edge server will pass traffic to the origin, you must configure WordPress to correctly identify the original visitor's IP address. Add the following lines to your wp-config.php file:
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
2. Restrict Firewall Access
To ensure absolute security, use ufw (Uncomplicated Firewall) on VPS 1 to only allow HTTP/HTTPS traffic coming explicitly from the static IP address of VPS 2.
sudo ufw allow from [VPS_2_EDGE_IP] to any port 80
sudo ufw allow from [VPS_2_EDGE_IP] to any port 443Step 2: Installing and Configuring Varnish Cache on the Edge Node (VPS 2)
Varnish Cache is an web application accelerator designed for content-heavy, dynamic websites like WordPress. It stores pages in memory to eliminate slow PHP processing and database queries entirely.
1. Install Varnish Cache
Update your repository and install Varnish on your Ubuntu-based Edge VPS:
sudo apt update && sudo apt install varnish -y2. Configure the Backend (vcl_recv and vcl_backend_response)
Edit the default Varnish configuration file located at /etc/varnish/default.vcl. This configuration instructs Varnish where to find the origin server and how to handle standard WordPress cookies that typically break caching mechanisms.
vcl 4.1;
backend default {
.host = "[VPS_1_ORIGIN_IP]";
.port = "80";
}
sub vcl_recv {
# Forward client IP
if (req.http.x-forwarded-for) {
set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
} else {
set req.http.X-Forwarded-For = client.ip;
}
# Exclude WordPress admin backend and preview pages from cache
if (req.url ~ "wp-admin" || req.url ~ "wp-login.php" || req.url ~ "preview=true") {
return (pass);
}
# Remove cookies that don't matter to frontend layout to maximize hit rate
set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]*([; ]*|$)", "");
if (req.http.Cookie ~ "wordpress_logged_in_") {
return (pass);
}
if (req.http.Cookie ~ "wp-postpass_") {
return (pass);
}
return (hash);
}Restart Varnish to apply the changes: sudo systemctl restart varnish.
Step 3: Setting Up Caddy Server as an SSL Gateway
While Varnish is ultra-fast, it does not natively support SSL/TLS encryption. To secure our setup, we use Caddy Server in front of Varnish. Caddy is a modern, memory-safe web server written in Go that handles automated Let's Encrypt SSL management natively.
1. Install Caddy
Execute the following commands to install Caddy on the edge server:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf '[https://dl.cloudsmith.io/public/caddy/stable/gpg.key](https://dl.cloudsmith.io/public/caddy/stable/gpg.key)' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf '[https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt](https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt)' | sudo tee /etc/apt/sources.list.min.d/caddy-stable.list
sudo apt update && sudo apt install caddy -y2. Configure the Caddyfile
Open /etc/caddy/Caddyfile and replace its content with the configuration below. This tells Caddy to listen on public standard web ports, generate an SSL certificate automatically, and proxy all traffic down to Varnish (which defaults to port 6081).
yourdomain.com {
reverse_proxy 127.0.0.1:6081 {
header_up Host {http.request.host}
header_up X-Real-IP {http.request.remote}
header_up X-Forwarded-Proto {http.request.scheme}
}
encode gzip zstd
}Reload Caddy to activate your production proxy environment: sudo systemctl reload caddy.
Step 4: Managing Cache Purges Automatically
A primary downside of high-efficiency caching is that when you update a post or change a layout in WordPress, visitors will continue to see old content until the cache expires. To solve this, implement automatic cache purging.
- Install a plugin such as Proxy Cache Purge on your WordPress instance.
- Navigate to the plugin settings and select Varnish as your proxy type.
- Enter the IP address of your Edge Server (VPS 2) as the purge target.
Now, whenever you publish a new article or update an existing page, WordPress will actively dispatch an HTTP PURGE request directly to Varnish, clearing that specific URL instantly.
Testing and Verification
To verify that your newly minted private mini-CDN is functioning optimally, run a remote execution test via your terminal using curl:
curl -I [https://yourdomain.com](https://yourdomain.com)Analyze the HTTP response headers carefully. You should see entries similar to this:
HTTP/2 200 OK
Server: Caddy
Via: 1.1 varnish (Varnish/7.0)
X-Varnish: 32770 12
Age: 45
The Server: Caddy confirmation demonstrates successful TLS handshake termination, while the presence of the X-Varnish and Age headers proves that the page is actively serving directly out of the edge node's RAM buffer.
Conclusion
By coupling the unmatched asset-caching speeds of Varnish with the effortless TLS modernism of Caddy, you have effectively established an isolated, private mini-CDN. This architecture eliminates bloated plugin dependencies on your WordPress origin, safeguards your database from unexpected concurrency spikes, and drops content delivery delays to absolute minimums—all while operating on cost-effective, multi-VPS infrastructure.
