The /health route returns JSON status for Docker Swarm to verify container health during deployment. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
9.0 KiB
Cloudflare Setup for Mylder Platform
Overview
Cloudflare configuration for https://mylder.io - Next.js 16 frontend with self-hosted backend (Supabase, n8n) via Dokploy/Traefik.
VPS: 149.102.155.84 Stack: Next.js 16 → Traefik → Cloudflare CDN
DNS Configuration
Step 1: Add Domain to Cloudflare
- Cloudflare Dashboard → Add Site → mylder.io
- Update nameservers at domain registrar to Cloudflare's nameservers
Step 2: DNS Records
| Type | Name | Target | Proxy Status | Notes |
|---|---|---|---|---|
| A | @ | 149.102.155.84 | Proxied (orange cloud) | Main site |
| A | www | 149.102.155.84 | Proxied | WWW redirect |
| A | dokploy | 149.102.155.84 | DNS Only (grey cloud) | Dokploy dashboard - skip CDN |
| A | supabase | 149.102.155.84 | DNS Only | Supabase API - skip CDN |
| A | n8n | 149.102.155.84 | DNS Only | n8n webhooks - skip CDN |
| A | gitea | 149.102.155.84 | DNS Only | Git push/pull - skip CDN |
| A | saas | 149.102.155.84 | DNS Only | Zulip chat - skip CDN |
Key Principle:
- Proxied (CDN): Frontend, static assets, public content
- DNS Only: Admin tools, APIs with authentication, real-time services
Step 3: Subdomain Wildcard (Optional)
If you need dynamic subdomains (e.g., tenant.mylder.io):
CNAME * @ (Proxied)
SSL/TLS Settings
Encryption Mode: Full (Strict)
Path: SSL/TLS → Overview → Full (strict)
Why: Traefik already provides Let's Encrypt certificates. Full (strict) ensures end-to-end encryption:
Browser → Cloudflare (encrypted) → Traefik (encrypted) → App
Edge Certificates
Path: SSL/TLS → Edge Certificates
- Always Use HTTPS: ON
- Minimum TLS Version: 1.2
- Opportunistic Encryption: ON
- TLS 1.3: ON
- Automatic HTTPS Rewrites: ON
- Certificate Transparency Monitoring: ON
Origin Server
Path: SSL/TLS → Origin Server
No action needed - Traefik manages Let's Encrypt certificates automatically.
Caching Rules
Default Cache Levels
Path: Caching → Configuration
- Caching Level: Standard
- Browser Cache TTL: Respect Existing Headers
- Always Online: ON (serves stale content if origin is down)
Cache Rules for Next.js
Path: Rules → Page Rules (legacy) or Cache Rules (new)**
Rule 1: Static Assets (Aggressive Caching)
URL Pattern: mylder.io/_next/static/*
Settings:
- Cache Level: Cache Everything
- Edge Cache TTL: 1 year
- Browser Cache TTL: 1 year
Rule 2: Images (Smart Caching)
URL Pattern: mylder.io/_next/image/*
Settings:
- Cache Level: Cache Everything
- Edge Cache TTL: 30 days
- Browser Cache TTL: 7 days
Rule 3: API Routes (No Cache)
URL Pattern: mylder.io/api/*
Settings:
- Cache Level: Bypass
Rule 4: Dynamic Pages (Smart Cache)
URL Pattern: mylder.io/*
Settings:
- Cache Level: Standard
- Edge Cache TTL: 2 hours
- Browser Cache TTL: 30 minutes
Next.js Cache Headers
Ensure your Next.js app sets proper headers:
// next.config.ts
const nextConfig = {
async headers() {
return [
{
source: '/_next/static/:path*',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
},
{
source: '/_next/image/:path*',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=2592000, stale-while-revalidate=86400',
},
],
},
];
},
};
Cache Purge Strategy
Path: Caching → Configuration → Purge Cache
- On Deploy: Purge Everything (via API or Dashboard)
- On Content Update: Purge by Tag/URL
- Emergency: Purge Everything button
API Purge (for CI/CD):
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \
-H "Authorization: Bearer {api_token}" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'
Speed Optimization
Auto Minify
Path: Speed → Optimization
- JavaScript: ON
- CSS: ON
- HTML: ON
Brotli Compression
Path: Speed → Optimization
- Brotli: ON (better than gzip)
Early Hints
Path: Speed → Optimization
- Early Hints: ON (preload critical resources)
Rocket Loader
Path: Speed → Optimization
- Rocket Loader: OFF (can conflict with React hydration)
Image Optimization
Path: Speed → Optimization → Image Resizing
- Polish: Lossless (or Lossy for smaller files)
- Mirage: ON (lazy loading for mobile)
- Image Resizing: Available on Pro+ plan
Next.js Image Optimization:
// next.config.ts
const nextConfig = {
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
minimumCacheTTL: 2592000, // 30 days
},
};
Security Settings
SSL/TLS
- Always Use HTTPS: ON
- HSTS: Enable with max-age=31536000, includeSubDomains
Firewall (WAF)
Path: Security → WAF
Managed Rules (Free Tier)
- Cloudflare Managed Ruleset: ON
- OWASP Core Ruleset: ON
Rate Limiting (Pro+ Plan)
Rule: API Protection
If: Request URL contains /api/
Then: Rate limit 100 requests per minute per IP
Bot Fight Mode (Free)
Path: Security → Bots
- Bot Fight Mode: ON
- Super Bot Fight Mode: Pro+ plan
Security Level
Path: Security → Settings
- Security Level: Medium (balance between security and UX)
Challenge Passage
Path: Security → Settings
- Challenge Passage: 30 minutes
Network Settings
HTTP/2 & HTTP/3
Path: Network
- HTTP/2: ON (default)
- HTTP/3 (QUIC): ON (faster connections)
- 0-RTT Connection Resumption: ON
WebSockets
Path: Network
- WebSockets: ON (required for real-time features)
gRPC
Path: Network
- gRPC: OFF (not needed for standard Next.js)
Analytics & Monitoring
Web Analytics
Path: Analytics & Logs → Web Analytics
- Enable Cloudflare Web Analytics (privacy-friendly, no cookie banner needed)
Performance Monitoring
- Core Web Vitals: Track LCP, FID, CLS
- Page Load Time: Monitor at edge
Real User Monitoring (RUM)
Add Cloudflare Zaraz or custom analytics:
<!-- Cloudflare Web Analytics -->
<script defer src='https://static.cloudflareinsights.com/beacon.min.js'
data-cf-beacon='{"token": "YOUR_TOKEN"}'></script>
Subdomain Configuration
Since you have multiple services, ensure proper routing:
Traefik Labels (Dokploy Docker Compose)
services:
app:
labels:
- "traefik.enable=true"
- "traefik.http.routers.mylder.rule=Host(`mylder.io`) || Host(`www.mylder.io`)"
- "traefik.http.routers.mylder.tls=true"
- "traefik.http.routers.mylder.tls.certresolver=letsencrypt"
WWW Redirect (Cloudflare)
Path: Rules → Redirect Rules
If: Hostname equals www.mylder.io
Then: Redirect to https://mylder.io$1 (301 permanent)
Testing Checklist
DNS Propagation
dig mylder.io
nslookup mylder.io
SSL Certificate
curl -I https://mylder.io
# Check for: HTTP/2 200, cf-cache-status header
Cache Verification
curl -I https://mylder.io/_next/static/[hash]/[file].js
# Look for: cf-cache-status: HIT
Performance
# Lighthouse
npx lighthouse https://mylder.io --view
# WebPageTest
# Visit https://www.webpagetest.org
Troubleshooting
521 Error (Web Server Down)
- Check Traefik is running:
docker ps | grep traefik - Check Dokploy deployment status
522 Error (Connection Timed Out)
- Verify firewall allows Cloudflare IPs
- Check VPS security groups
525 Error (SSL Handshake Failed)
- Ensure SSL/TLS mode is "Full (strict)"
- Verify Traefik has valid Let's Encrypt certificate
Cache Not Working
- Check Cache-Control headers in response
- Verify Page Rules are active
- Clear Cloudflare cache and test
Mixed Content Warnings
- Enable "Automatic HTTPS Rewrites"
- Update hardcoded http:// URLs to https://
Cost Optimization
Free Plan Limits
- 100k requests/day (~3M/month)
- Unlimited bandwidth
- Basic DDoS protection
- Shared SSL certificate
- 3 Page Rules
When to Upgrade to Pro ($20/month)
- Need more than 3 Page Rules
- Want Image Optimization (Polish, Mirage)
- Need custom SSL certificate
- Require advanced WAF rules
- Want priority support
Enterprise Features (for future)
- Cloudflare Workers (serverless at edge)
- Load Balancing
- Argo Smart Routing
- Custom WAF rules
Next Steps
- Immediate: DNS + SSL/TLS setup
- Week 1: Caching rules + performance testing
- Month 1: Monitor analytics, tune cache rules
- Quarter 1: Consider Workers for edge logic (see cloudflare-workers-roadmap.md)