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.euadded 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 returnCache-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:
- Single URL: Cloudflare dashboard > Caching > Purge Cache > Custom Purge > Enter URL
- All media: Purge by prefix:
https://freezedesign.eu/media/ - 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¶
- Verify the resource URL matches a Cache Rule path
- Check
Cache-Controlheaders from origin:curl -sI https://freezedesign.eu/static/test.css - Ensure Cloudflare proxy is enabled (orange cloud) for the DNS record
- Check "Development Mode" is not enabled (disables caching)
Origin Server Overloaded¶
If the origin server is under heavy load:
- Enable "Under Attack Mode" temporarily (adds JavaScript challenge)
- Check Cache Analytics for cache hit ratio (target: >80% for static assets)
- Consider increasing Edge TTL for media files if content rarely changes