Cloudflare CDN Configuration for VPS

Cloudflare acts as a reverse proxy and CDN in front of your VPS, providing DDoS protection, SSL termination, global caching, and a web application firewall without any changes to your server software. This guide covers adding your domain to Cloudflare, configuring DNS, SSL modes, caching rules, page rules, and WAF settings to improve both performance and security for VPS-hosted websites.

Prerequisites

  • A domain name registered at any registrar
  • A VPS with a web server running (Nginx or Apache)
  • A Cloudflare account (free tier works for most configurations)
  • Access to your domain registrar's nameserver settings

Adding Your Domain to Cloudflare

  1. Log into Cloudflare Dashboard and click Add a Site
  2. Enter your domain (e.g., example.com) and click Add Site
  3. Select a plan (Free is sufficient for CDN and basic WAF)
  4. Cloudflare will scan your existing DNS records — review and confirm them
  5. Update your domain's nameservers at your registrar to the two Cloudflare nameservers provided:
    • aria.ns.cloudflare.com
    • bob.ns.cloudflare.com (these will differ per account)

Nameserver propagation takes up to 24 hours but is usually under an hour.

DNS Configuration

In the Cloudflare DNS dashboard:

TypeNameContentProxy
A@YOUR_SERVER_IPProxied (orange cloud)
AwwwYOUR_SERVER_IPProxied (orange cloud)
AmailMAIL_SERVER_IPDNS only (grey cloud)
MX@mail.example.comDNS only

Important: Mail servers, FTP, and SSH access records should use DNS only (grey cloud) — proxying these will break them.

Proxied records hide your actual server IP from the public, providing DDoS protection.

Using the Cloudflare API to manage DNS records:

# Get your Zone ID
curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=example.com" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" | jq '.result[0].id'

# Add an A record via API
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "type": "A",
    "name": "api",
    "content": "YOUR_SERVER_IP",
    "ttl": 1,
    "proxied": true
  }'

SSL/TLS Mode Selection

Navigate to SSL/TLS > Overview and choose the appropriate mode:

  • Off: No SSL — avoid unless testing
  • Flexible: SSL between visitor and Cloudflare; plain HTTP to your server (not recommended for production)
  • Full: SSL to your server using any certificate (including self-signed)
  • Full (Strict): SSL to your server with a valid certificate — recommended for production

For Full (Strict), install a free origin certificate on your VPS:

# In Cloudflare: SSL/TLS > Origin Server > Create Certificate
# Download the certificate and key, then install:

sudo mkdir -p /etc/ssl/cloudflare
sudo nano /etc/ssl/cloudflare/origin.pem    # Paste the certificate
sudo nano /etc/ssl/cloudflare/origin.key    # Paste the private key

# Nginx configuration
sudo nano /etc/nginx/sites-available/example.com
server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate /etc/ssl/cloudflare/origin.pem;
    ssl_certificate_key /etc/ssl/cloudflare/origin.key;

    # Only allow Cloudflare IPs (optional, for extra security)
    # See: https://www.cloudflare.com/ips/
}

Enable Always Use HTTPS under SSL/TLS > Edge Certificates to redirect all HTTP visitors to HTTPS.

Caching Rules and Cache Control

Navigate to Caching > Configuration:

  • Caching Level: Standard (caches based on file extension)
  • Browser Cache TTL: Respect Existing Headers (recommended), or set a custom value
  • Always Online: Enable to serve cached pages if your origin is down

Set proper cache headers on your Nginx server:

# Cache static assets aggressively
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2|svg)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

# Don't cache dynamic content
location ~ \.php$ {
    add_header Cache-Control "no-store, no-cache, must-revalidate";
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    # ... other fastcgi params
}

Create a Cloudflare Cache Rule (under Rules > Cache Rules):

  • Expression: (http.host eq "example.com" and starts_with(http.request.uri.path, "/static/"))
  • Cache Status: Eligible for cache
  • Edge TTL: Override — 30 days

Purge the Cloudflare cache via API:

# Purge everything
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"purge_everything":true}'

# Purge specific files
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"files":["https://example.com/style.css","https://example.com/app.js"]}'

Page Rules and Transform Rules

Page Rules (under Rules > Page Rules) — up to 3 on the free plan:

URL PatternSettingValue
example.com/admin/*Cache LevelBypass
*.example.com/*Always Use HTTPSOn
example.com/api/*Cache LevelBypass

Redirect Rules (under Rules > Redirect Rules) — for www to non-www:

  • Expression: (http.host eq "www.example.com")
  • Redirect to: https://example.com${http.request.uri.path}
  • Status code: 301

Web Application Firewall (WAF)

Navigate to Security > WAF:

  1. Enable Cloudflare Managed Ruleset — blocks common web exploits (SQLi, XSS, etc.)
  2. Set Security Level under Security > Settings to Medium or High
  3. Enable Bot Fight Mode to block automated scrapers

Create a custom WAF rule to block countries:

  • Expression: (ip.geoip.country in {"CN" "RU"}) and not (ip.geoip.country eq "US")
  • Action: Block (or Challenge)

Rate limiting (under Security > WAF > Rate Limiting Rules):

  • Expression: (http.request.uri.path contains "/wp-login.php")
  • Rate: 5 requests per 10 seconds
  • Action: Block for 1 hour

Performance Optimization

# Enable in Cloudflare dashboard:
# Speed > Optimization:
# - Auto Minify: HTML, CSS, JS
# - Brotli compression: On
# - HTTP/2: Enabled by default
# - HTTP/3 (QUIC): Enable
# - Rocket Loader: On (for async JS loading)
# - Polish: Lossy (compresses images)
# - Mirage: On (lazy loading for mobile)

Enable Argo Smart Routing (paid add-on) for optimized routing through Cloudflare's network.

Check performance with:

# Verify Cloudflare is handling requests (look for CF-RAY header)
curl -I https://example.com | grep -i "cf-ray\|server\|x-cache"

Troubleshooting

Error 521 (Web server is down)

# Cloudflare cannot reach your origin — check Nginx is running
systemctl status nginx
# Verify port 80/443 are open
ufw status

Error 524 (Timeout)

# Your server is too slow to respond — increase Cloudflare's timeout
# Under Network > Network > Proxy Read Timeout (max 6000s on Enterprise)
# Alternatively, optimize your application response time

Caching dynamic content

# Add Cache-Control: no-store to sensitive pages
# Or create a Page Rule to bypass cache for /admin/* and /account/*

SSL errors with origin certificate

# Ensure Full (Strict) mode is selected in Cloudflare
# Verify certificate is not expired
openssl x509 -in /etc/ssl/cloudflare/origin.pem -noout -dates

Conclusion

Cloudflare provides a powerful CDN, WAF, and DDoS protection layer for VPS-hosted websites with minimal configuration. By selecting the correct SSL mode, configuring cache rules to match your application's needs, and enabling WAF rulesets, you significantly improve both performance and security without touching your server infrastructure.