Add health endpoint for Swarm health checks
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>
This commit is contained in:
380
docs/cloudflare-setup.md
Normal file
380
docs/cloudflare-setup.md
Normal file
@@ -0,0 +1,380 @@
|
||||
# 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
|
||||
1. Cloudflare Dashboard → Add Site → mylder.io
|
||||
2. 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:
|
||||
|
||||
```typescript
|
||||
// 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):**
|
||||
```bash
|
||||
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:**
|
||||
```typescript
|
||||
// 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)
|
||||
```yaml
|
||||
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:
|
||||
```html
|
||||
<!-- 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)
|
||||
```yaml
|
||||
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
|
||||
```bash
|
||||
dig mylder.io
|
||||
nslookup mylder.io
|
||||
```
|
||||
|
||||
### SSL Certificate
|
||||
```bash
|
||||
curl -I https://mylder.io
|
||||
# Check for: HTTP/2 200, cf-cache-status header
|
||||
```
|
||||
|
||||
### Cache Verification
|
||||
```bash
|
||||
curl -I https://mylder.io/_next/static/[hash]/[file].js
|
||||
# Look for: cf-cache-status: HIT
|
||||
```
|
||||
|
||||
### Performance
|
||||
```bash
|
||||
# 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
|
||||
|
||||
1. **Immediate:** DNS + SSL/TLS setup
|
||||
2. **Week 1:** Caching rules + performance testing
|
||||
3. **Month 1:** Monitor analytics, tune cache rules
|
||||
4. **Quarter 1:** Consider Workers for edge logic (see cloudflare-workers-roadmap.md)
|
||||
|
||||
## References
|
||||
|
||||
- [Cloudflare DNS Docs](https://developers.cloudflare.com/dns/)
|
||||
- [Next.js Cloudflare Deployment](https://nextjs.org/docs/app/building-your-application/deploying)
|
||||
- [Cloudflare Cache API](https://developers.cloudflare.com/cache/)
|
||||
Reference in New Issue
Block a user