Skip to content

Cloudflare CDN Configuration for Freeze Design

This guide documents the Cloudflare CDN configuration for freezedesign.eu. Cloudflare acts as a reverse proxy and CDN, caching static assets at edge locations to reduce origin server load and improve page load times.

Prerequisites

  • Cloudflare account (free plan is sufficient)
  • Domain freezedesign.eu added to Cloudflare
  • Nameservers updated at your registrar to Cloudflare's assigned nameservers
  • Origin server (Hetzner VPS) running and accessible

DNS Records

Configure the following DNS records in the Cloudflare dashboard. All records that serve web traffic should have the Proxied (orange cloud) status enabled.

Type Name Content Proxy Status TTL
A @ <SERVER_IP> Proxied Auto
CNAME www freezedesign.eu Proxied Auto
CNAME staging <SERVER_IP> Proxied Auto

Optional (for direct media serving via DigitalOcean Spaces):

Type Name Content Proxy Status TTL
CNAME cdn freezedesign-media.ams3.digitaloceanspaces.com Proxied Auto

Note: Replace <SERVER_IP> with the actual Hetzner VPS IP address.

SSL/TLS Configuration

Navigate to SSL/TLS in the Cloudflare dashboard and configure:

SSL Mode

  • Encryption mode: Full (strict)
  • This requires a valid SSL certificate on the origin server (Let's Encrypt).
  • Full (strict) ensures encrypted connections on both legs (client-to-Cloudflare and Cloudflare-to-origin).

Edge Certificates

  • Always Use HTTPS: Enabled
  • Redirects all HTTP requests to HTTPS.
  • Automatic HTTPS Rewrites: Enabled
  • Fixes mixed content issues by rewriting HTTP URLs in responses to HTTPS.
  • Minimum TLS Version: 1.2
  • Rejects connections using TLS 1.0 or 1.1 (deprecated protocols).
  • TLS 1.3: Enabled
  • Enables the latest TLS version for improved performance and security.

Cache Rules

Navigate to Caching > Cache Rules and create the following 3 rules in order. Cache Rules replace the deprecated Page Rules for cache configuration.

Rule 1: Next.js Static Assets

Next.js generates content-hashed filenames for static assets, making them safe to cache indefinitely.

  • Rule name: Next.js Static Assets
  • When incoming requests match:
  • Field: URI Path
  • Operator: starts with
  • Value: /_next/static/
  • Then:
  • Cache eligibility: Eligible for cache
  • Edge TTL: Override origin, TTL: 365 days
  • Browser TTL: Override origin, TTL: 365 days

Rationale: Content-hashed filenames (e.g., /_next/static/chunks/abc123.js) change on every build, so stale cache is never served. The immutable directive from Nginx Cache-Control headers also prevents unnecessary revalidation.

Rule 2: Django Static Files

Django's collectstatic generates versioned filenames via ManifestStaticFilesStorage.

  • Rule name: Django Static Files
  • When incoming requests match:
  • Field: URI Path
  • Operator: starts with
  • Value: /static/
  • Then:
  • Cache eligibility: Eligible for cache
  • Edge TTL: Override origin, TTL: 30 days
  • Browser TTL: Override origin, TTL: 30 days

Rationale: Django static files are versioned via content hashing in the manifest. 30 days balances cache performance with the ability to deploy admin UI updates.

Rule 3: Product Media Images

Product images (originals and WebP variants) served from /media/.

  • Rule name: Product Media Images
  • When incoming requests match:
  • Field: URI Path
  • Operator: starts with
  • Value: /media/
  • Then:
  • Cache eligibility: Eligible for cache
  • Edge TTL: Override origin, TTL: 7 days
  • Browser TTL: Override origin, TTL: 7 days

Rationale: Media images can change when products are updated. 7 days provides good CDN performance while ensuring updated images propagate within a week. Use Cloudflare cache purge for immediate updates if needed.

Cache Rules Alignment with Nginx

These Cache Rules align with the Nginx configuration in nginx/nginx.conf:

URL Path Nginx expires Nginx Cache-Control CF Edge TTL CF Browser TTL
/_next/static/ 365d public, immutable 365 days 365 days
/static/ 30d public, immutable 30 days 30 days
/media/ 7d public 7 days 7 days
/api/ - no-store Not cached Not cached
/admin/ - no-store Not cached Not cached

Important: The /api/ and /admin/ paths return Cache-Control: no-store, which Cloudflare respects by default. No additional Cache Rules are needed to prevent caching of dynamic content.

Speed Settings

Navigate to Speed > Optimization and configure:

  • Auto Minify: Enable for JavaScript, CSS, and HTML
  • Brotli: Enabled (better compression than gzip for supported browsers)
  • Early Hints: Enabled (preloads assets for faster page loads)

Security Settings

Navigate to Security and configure:

  • Bot Fight Mode: Enabled
  • Security Level: Medium
  • Challenge Passage: 30 minutes
  • Browser Integrity Check: Enabled

Verification

After configuring Cloudflare, verify that caching works correctly.

Check Cache Status Headers

# Check Next.js static assets (should show cf-cache-status: HIT after first request)
curl -sI https://freezedesign.eu/_next/static/css/app.css 2>&1 | grep -i "cf-cache-status"

# Check Django static files
curl -sI https://freezedesign.eu/static/admin/css/base.css 2>&1 | grep -i "cf-cache-status"

# Check media files
curl -sI https://freezedesign.eu/media/products/colors/example.jpg 2>&1 | grep -i "cf-cache-status"

# Check API is NOT cached
curl -sI https://freezedesign.eu/api/health/ 2>&1 | grep -i "cf-cache-status"
# Should show DYNAMIC or not present

Expected cf-cache-status Values

Value Meaning
HIT Served from Cloudflare edge cache
MISS Not in cache, fetched from origin (next request should HIT)
EXPIRED Was cached but expired, re-fetched from origin
DYNAMIC Not eligible for caching (e.g., API responses)
BYPASS Cache bypassed due to configuration

Purge Cache

If you need to immediately update cached content:

  1. Single URL: Cloudflare dashboard > Caching > Purge Cache > Custom Purge > Enter URL
  2. All media: Purge by prefix: https://freezedesign.eu/media/
  3. Everything: Purge Everything (use sparingly, causes temporary origin load spike)
# Verify purge worked (should show MISS then HIT on second request)
curl -sI https://freezedesign.eu/media/products/colors/example.jpg 2>&1 | grep -i "cf-cache-status"

Troubleshooting

SSL Errors (525/526)

  • Error 525: SSL handshake failed. Verify Let's Encrypt certificates are valid and Nginx SSL configuration is correct.
  • Error 526: Invalid SSL certificate. Ensure Full (strict) mode is set and origin certificate is trusted (Let's Encrypt certificates are).

Cache Not Working

  1. Verify the resource URL matches a Cache Rule path
  2. Check Cache-Control headers from origin: curl -sI https://freezedesign.eu/static/test.css
  3. Ensure Cloudflare proxy is enabled (orange cloud) for the DNS record
  4. Check "Development Mode" is not enabled (disables caching)

Origin Server Overloaded

If the origin server is under heavy load:

  1. Enable "Under Attack Mode" temporarily (adds JavaScript challenge)
  2. Check Cache Analytics for cache hit ratio (target: >80% for static assets)
  3. Consider increasing Edge TTL for media files if content rarely changes