Table of Contents
Adding a Domain to Cloudflare
Step 1: Create a Cloudflare Account
Sign up at dash.cloudflare.com. The free plan includes DNS management, DDoS protection, SSL, and basic WAF rules — more than enough for most sites.
Step 2: Add Your Domain
- Click "Add a Site" in the dashboard
- Enter your domain (e.g.,
example.com) - Select a plan (Free is fine to start)
- Cloudflare will scan your existing DNS records automatically
Step 3: Update Your Nameservers
Cloudflare assigns you two nameservers. Update them at your domain registrar:
# Example Cloudflare nameservers
ns1.cloudflare.com
ns2.cloudflare.com
# At your registrar (GoDaddy, Namecheap, etc.):
# Replace existing nameservers with the ones Cloudflare provides
Plan Comparison
| Feature | Free | Pro ($20/mo) | Business ($200/mo) |
|---|---|---|---|
| DNS Management | ✓ | ✓ | ✓ |
| DDoS Protection | ✓ | ✓ | ✓ |
| Universal SSL | ✓ | ✓ | ✓ |
| WAF Managed Rules | Limited | ✓ | ✓ |
| Bot Management | Basic | Super Bot Fight | Advanced |
| Page Rules | 3 | 20 | 50 |
| Image Optimization | ✗ | ✓ | ✓ |
| Custom WAF Rules | 5 | 20 | 100 |
| Cloudflare Tunnels | ✓ | ✓ | ✓ |
DNS Record Management
Cloudflare acts as your authoritative DNS provider. Understanding record types is essential:
Common DNS Record Types
| Type | Purpose | Example |
|---|---|---|
| A | Maps domain to IPv4 address | example.com → 203.0.113.50 |
| AAAA | Maps domain to IPv6 address | example.com → 2001:db8::1 |
| CNAME | Alias to another domain | www → example.com |
| MX | Mail server routing | mail.example.com (Priority: 10) |
| TXT | Text records (SPF, DKIM, verification) | v=spf1 include:_spf.google.com ~all |
| SRV | Service location (VoIP, LDAP, etc.) | _sip._tcp.example.com |
| NS | Nameserver delegation | ns1.cloudflare.com |
| CAA | SSL certificate authority authorization | 0 issue "letsencrypt.org" |
Adding Records
# A Record — point domain to your server IP
Type: A
Name: @ (or subdomain like "dev")
Content: 203.0.113.50
Proxy: Proxied (orange cloud)
TTL: Auto
# CNAME — www redirect
Type: CNAME
Name: www
Content: example.com
Proxy: Proxied
# MX — email routing (must be DNS Only / grey cloud)
Type: MX
Name: @
Content: mail.example.com
Priority: 10
Proxy: DNS Only (grey cloud)
# TXT — SPF record for email authentication
Type: TXT
Name: @
Content: v=spf1 include:_spf.google.com ~all
# TXT — DKIM for email signing
Type: TXT
Name: google._domainkey
Content: v=DKIM1; k=rsa; p=MIIBIjANBgkqh...
# TXT — DMARC policy
Type: TXT
Name: _dmarc
Content: v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com
@) — something standard DNS doesn't support. This is called CNAME flattening, and it resolves the CNAME to an A record at the edge.
Managing DNS with the Cloudflare API
# List all DNS records
curl -X GET "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json"
# Create a new A record
curl -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": "dev",
"content": "203.0.113.50",
"ttl": 1,
"proxied": true
}'
# Delete a record
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/ZONE_ID/dns_records/RECORD_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
Proxy Modes: Orange Cloud vs Grey Cloud
The proxy toggle on each DNS record is one of the most important Cloudflare concepts:
| Feature | Proxied (Orange Cloud) | DNS Only (Grey Cloud) |
|---|---|---|
| Traffic flows through | Cloudflare's network | Direct to your server |
| Origin IP hidden | ✓ Yes | ✗ Exposed |
| DDoS protection | ✓ Active | ✗ None |
| WAF / Firewall | ✓ Active | ✗ None |
| Caching | ✓ Active | ✗ None |
| SSL termination | ✓ At edge | ✗ At origin |
| Use for email (MX) | ✗ Never | ✓ Required |
| Use for FTP/SSH | ✗ Won't work | ✓ Required |
| Ports supported | 80, 443 + select others | All ports |
When to Use Each Mode
# Orange Cloud (Proxied) — web traffic
example.com A 203.0.113.50 Proxied ✓
www.example.com CNAME example.com Proxied ✓
app.example.com A 203.0.113.50 Proxied ✓
# Grey Cloud (DNS Only) — non-web services
mail.example.com A 203.0.113.50 DNS Only
ftp.example.com A 203.0.113.50 DNS Only
ssh.example.com A 203.0.113.50 DNS Only
@ MX mail.example.com DNS Only
SSL/TLS Configuration
Cloudflare provides free SSL certificates and offers multiple encryption modes. Choosing the right one is critical for security and avoiding redirect loops.
SSL/TLS Encryption Modes
| Mode | Browser ↔ Cloudflare | Cloudflare ↔ Origin | Use When |
|---|---|---|---|
| Off | HTTP | HTTP | Never recommended |
| Flexible | HTTPS | HTTP | Origin has no SSL cert |
| Full | HTTPS | HTTPS (any cert) | Self-signed cert on origin |
| Full (Strict) | HTTPS | HTTPS (valid cert) | Let's Encrypt or Cloudflare Origin cert |
Cloudflare Origin Certificates
If you can't use Let's Encrypt (e.g., behind a tunnel), use Cloudflare's free Origin Certificates — valid for up to 15 years, trusted only by Cloudflare's edge:
# 1. Generate in Dashboard: SSL/TLS → Origin Server → Create Certificate
# 2. Download the cert and key files
# 3. Install on your origin server (Nginx example):
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/cloudflare/origin.pem;
ssl_certificate_key /etc/ssl/cloudflare/origin-key.pem;
# Only accept connections from Cloudflare IPs
# (use with set_real_ip_from directives)
}
Always Use HTTPS
# Enable in Dashboard: SSL/TLS → Edge Certificates
# Toggle ON: "Always Use HTTPS"
# This adds an automatic 301 redirect:
# http://example.com → https://example.com
# Equivalent Page Rule:
# URL: http://*example.com/*
# Setting: Always Use HTTPS
HSTS (HTTP Strict Transport Security)
# Enable in Dashboard: SSL/TLS → Edge Certificates → HSTS
# Recommended settings:
# Max-Age: 6 months (15768000)
# Include subdomains: Yes (if all subdomains support HTTPS)
# Preload: Yes (after confirming all subdomains work)
# No-Sniff: Yes
# This adds the header:
# Strict-Transport-Security: max-age=15768000; includeSubDomains; preload
Minimum TLS Version
# Dashboard: SSL/TLS → Edge Certificates → Minimum TLS Version
# Recommended: TLS 1.2
# This blocks connections using TLS 1.0 and 1.1 (known vulnerabilities)
Firewall Rules & WAF
Cloudflare's firewall lets you create custom rules to block, challenge, or allow traffic based on dozens of criteria. The Web Application Firewall (WAF) adds managed rulesets for known attack patterns.
Custom Firewall Rules (WAF Custom Rules)
Navigate to Security → WAF → Custom Rules to create rules using Cloudflare's expression builder or wirefilter syntax:
Block Specific Countries
# Expression:
(ip.geoip.country in {"CN" "RU" "KP"})
# Action: Block
# This blocks all traffic from China, Russia, and North Korea
Challenge Suspicious Requests
# Block requests to admin paths from non-whitelisted IPs:
(http.request.uri.path contains "/admin" and
not ip.src in {203.0.113.50 198.51.100.0/24})
# Action: Block
# Challenge requests with no user agent:
(http.user_agent eq "")
# Action: Managed Challenge
Protect Login Pages
# Challenge all login attempts:
(http.request.uri.path eq "/login" or
http.request.uri.path eq "/wp-login.php" or
http.request.uri.path eq "/administrator")
# Action: Managed Challenge
Allow Known Good Traffic
# Skip all security for trusted IPs (monitoring, CI/CD):
(ip.src in {198.51.100.10 203.0.113.0/24})
# Action: Skip (select which features to skip: WAF, Rate Limiting, etc.)
Block Known Attack Patterns
# Block SQL injection attempts in query strings:
(http.request.uri.query contains "UNION SELECT" or
http.request.uri.query contains "1=1" or
http.request.uri.query contains "../" or
http.request.uri.query contains "etc/passwd")
# Action: Block
# Block requests with suspiciously long URIs (buffer overflow attempts):
(len(http.request.uri) gt 2048)
# Action: Block
WAF Managed Rulesets
Cloudflare provides pre-built rulesets that protect against common vulnerabilities:
- Cloudflare Managed Ruleset — SQL injection, XSS, RCE, and other OWASP Top 10 attacks
- Cloudflare OWASP Core Ruleset — Based on the ModSecurity OWASP CRS
- Cloudflare Leaked Credentials Detection — Checks login requests against known breached credential databases
- Cloudflare Free Managed Ruleset — Basic protections available on the free plan
# Enable managed rulesets:
# Dashboard: Security → WAF → Managed Rules
# Toggle ON the rulesets you need
# Click "Browse Rules" to customize individual rule actions
# Common overrides:
# - Set specific rules to "Log" instead of "Block" during testing
# - Disable rules that cause false positives on your application
# - Increase sensitivity for login/admin pages
IP Access Rules
# Dashboard: Security → WAF → Tools → IP Access Rules
# Block a single IP:
# IP: 192.0.2.100 → Action: Block → Zone: example.com
# Block an IP range:
# IP: 192.0.2.0/24 → Action: Block
# Whitelist your server/office:
# IP: 203.0.113.50 → Action: Allow
# Challenge an entire ASN:
# ASN: AS12345 → Action: Managed Challenge
# Block an entire country:
# Country: XX → Action: Block
DDoS Protection
Cloudflare provides always-on DDoS protection on all plans, including the free tier. Their network absorbs attack traffic at the edge before it reaches your origin server.
How Cloudflare DDoS Protection Works
- Anycast Network — Traffic is distributed across 300+ data centers worldwide, absorbing volumetric attacks
- Autonomous Detection — Machine learning identifies and mitigates attacks in under 3 seconds
- No Manual Intervention — Protection is always active, no need to "turn it on" during an attack
- Unmetered Mitigation — Cloudflare doesn't charge extra for attack traffic, even on the free plan
Layer 3/4 DDoS (Network Layer)
# Protects against:
# - SYN floods
# - UDP floods
# - ICMP floods
# - Amplification attacks (DNS, NTP, memcached)
# - Protocol attacks
# Configuration: Security → DDoS → Network-layer DDoS Attack Protection
# Sensitivity: High (recommended)
# Action: Block (or Managed Challenge for borderline traffic)
Layer 7 DDoS (Application Layer)
# Protects against:
# - HTTP floods (massive request volume)
# - Slowloris attacks
# - Cache-busting attacks
# - Credential stuffing
# Configuration: Security → DDoS → HTTP DDoS Attack Protection
# Recommended settings:
# Sensitivity: Medium-High
# Action: Managed Challenge (lets legitimate users through)
# Override for specific paths (e.g., API endpoints):
# Create a DDoS override rule:
# Expression: (http.request.uri.path contains "/api/")
# Sensitivity: High
# Action: Block
Under Attack Mode
# For active attacks — enables JavaScript challenge for ALL visitors:
# Dashboard: Overview → Quick Actions → Under Attack Mode → ON
# What it does:
# 1. Shows an interstitial "Checking your browser" page
# 2. Validates the visitor's browser with a JS challenge
# 3. Issues a clearance cookie (valid for ~30 minutes)
# 4. Legitimate users pass through after 5 seconds
# IMPORTANT: Only enable during active attacks
# Disable afterward — it adds latency and may affect:
# - API consumers
# - Monitoring services
# - Webhooks and callbacks
# - Search engine crawlers
iptables or ufw to only accept connections from Cloudflare's IP ranges.
Lock Down Your Origin
# UFW — only allow Cloudflare IPs to reach port 443
sudo ufw default deny incoming
sudo ufw allow from 173.245.48.0/20 to any port 443
sudo ufw allow from 103.21.244.0/22 to any port 443
sudo ufw allow from 103.22.200.0/22 to any port 443
sudo ufw allow from 103.31.4.0/22 to any port 443
sudo ufw allow from 141.101.64.0/18 to any port 443
sudo ufw allow from 108.162.192.0/18 to any port 443
sudo ufw allow from 190.93.240.0/20 to any port 443
sudo ufw allow from 188.114.96.0/20 to any port 443
sudo ufw allow from 197.234.240.0/22 to any port 443
sudo ufw allow from 198.41.128.0/17 to any port 443
sudo ufw allow from 162.158.0.0/15 to any port 443
sudo ufw allow from 104.16.0.0/13 to any port 443
sudo ufw allow from 104.24.0.0/14 to any port 443
sudo ufw allow from 172.64.0.0/13 to any port 443
sudo ufw allow from 131.0.72.0/22 to any port 443
# Don't forget SSH access for yourself
sudo ufw allow from YOUR_IP to any port 22
sudo ufw enable
# Nginx — restrict origin to Cloudflare IPs
# (see the set_real_ip_from directives in nginx.conf)
Bot Management
Cloudflare identifies and classifies bot traffic using machine learning, behavior analysis, and fingerprinting. You can then take action based on bot scores and categories.
Bot Score
Every request receives a bot score from 1-99:
- 1-29: Likely automated (bots)
- 30-49: Uncertain — could be either
- 50-99: Likely human
Super Bot Fight Mode (Pro+)
# Dashboard: Security → Bots → Configure Super Bot Fight Mode
# Options:
# Definitely automated → Block / Managed Challenge / Allow
# Likely automated → Block / Managed Challenge / Allow
# Verified bots (Google, Bing) → Allow (recommended)
# Recommended settings:
# Definitely automated: Block
# Likely automated: Managed Challenge
# Verified bots: Allow
Custom Bot Rules with Firewall
# Block bots on sensitive pages (Free plan):
(cf.bot_management.score lt 30 and
http.request.uri.path contains "/api/")
# Action: Block
# Challenge uncertain traffic on login pages:
(cf.bot_management.score lt 50 and
http.request.uri.path eq "/login")
# Action: Managed Challenge
# Allow verified bots (search engines) everywhere:
(cf.bot_management.verified_bot)
# Action: Skip (all remaining custom rules)
# Block AI scrapers by user agent:
(http.user_agent contains "GPTBot" or
http.user_agent contains "CCBot" or
http.user_agent contains "ChatGPT" or
http.user_agent contains "anthropic") and
not http.request.uri.path in {"/index.html" "/about.html"}
# Action: Block
Verified Bots
Cloudflare maintains a list of verified bots (search engine crawlers, monitoring services, etc.) that can be explicitly allowed or blocked:
- Googlebot — Google's search crawler
- Bingbot — Microsoft's search crawler
- Slurp — Yahoo's crawler
- DuckDuckBot — DuckDuckGo's crawler
- Monitoring services — UptimeRobot, Pingdom, etc.
- Social media — Facebook, Twitter link previews
Rate Limiting
Protect your origin from abuse by limiting how many requests a single IP can make in a given time window.
Creating Rate Limiting Rules
# Dashboard: Security → WAF → Rate Limiting Rules → Create Rule
# Example 1: Protect login from brute force
# Expression: (http.request.uri.path eq "/login")
# Rate: 5 requests per 1 minute
# Per: IP
# Action: Block for 10 minutes
# Response: Custom JSON: {"error": "Too many login attempts"}
# Example 2: API rate limiting
# Expression: (http.request.uri.path contains "/api/")
# Rate: 100 requests per 1 minute
# Per: IP
# Action: Block for 1 minute
# Response: 429 Too Many Requests
# Example 3: General site-wide protection
# Expression: (true)
# Rate: 1000 requests per 10 seconds
# Per: IP
# Action: Managed Challenge
# Mitigation timeout: 60 seconds
Advanced Rate Limiting Characteristics
# Count by characteristics (Pro+):
# - IP address (default)
# - IP + headers
# - Country
# - IP + path
# - Custom header values (e.g., API key)
# Example: Rate limit per API key:
# Expression: (http.request.uri.path contains "/api/")
# Count by: http.request.headers["x-api-key"]
# Rate: 500 requests per 1 minute
# Action: Block
Page Rules & Redirects
Page Rules let you apply specific Cloudflare settings to matching URL patterns. Free plan includes 3 rules; Pro adds 20.
Common Page Rules
# 1. Always Use HTTPS (most important rule)
URL: http://*example.com/*
Setting: Always Use HTTPS
# 2. Redirect www to non-www (or vice versa)
URL: www.example.com/*
Setting: Forwarding URL (301)
Destination: https://example.com/$1
# 3. Cache everything (static site)
URL: example.com/*
Setting: Cache Level → Cache Everything
Edge Cache TTL: 1 month
# 4. Bypass cache for admin/API
URL: example.com/admin/*
Settings:
Cache Level → Bypass
Security Level → High
Disable Performance
# 5. Force specific SSL mode per subdomain
URL: api.example.com/*
Setting: SSL → Full (Strict)
Bulk Redirects (Replaces Page Rules for Redirects)
For large numbers of redirects, use Bulk Redirects instead of Page Rules:
# Dashboard: Rules → Redirect Rules → Create Rule
# Example redirect rule:
# When: (http.request.full_uri eq "https://example.com/old-page")
# Then: Redirect to https://example.com/new-page
# Status: 301 (Permanent)
# Preserve query string: Yes
# Or use Bulk Redirect Lists:
# Dashboard: Rules → Lists → Create List (type: Redirect)
# Import CSV with columns: source, target, status code
Caching & Performance
Cloudflare caches static assets at 300+ edge locations worldwide, reducing load on your origin server and improving page load times for visitors.
What Cloudflare Caches by Default
# Cached automatically (static assets):
.js .css .png .jpg .jpeg .gif .ico .svg
.woff .woff2 .ttf .eot .webp .avif .pdf
# NOT cached by default (dynamic content):
.html .php .asp .aspx .json .xml
# To cache HTML (static sites), use a Page Rule:
# Cache Level → Cache Everything
Cache Settings
# Dashboard: Caching → Configuration
# Browser Cache TTL:
# How long browsers cache assets before rechecking
# Recommended: "Respect Existing Headers" (let your server control it)
# Or set a minimum: 4 hours for most sites
# Caching Level:
# Standard (default) — caches based on query string
# No Query String — ignores query strings
# Simplified — treats all query strings as the same resource
# Edge Cache TTL:
# How long Cloudflare caches at the edge
# Set via Page Rules or Cache-Control headers from origin
Purging the Cache
# Purge everything (nuclear option):
# Dashboard: Caching → Configuration → Purge Everything
# Purge specific URLs:
# Dashboard: Caching → Configuration → Custom Purge
# Enter URLs one per line
# Purge via API:
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/css/style.css"]}'
# Purge by prefix:
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 '{"prefixes": ["example.com/images/"]}'
# Purge everything via API:
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}'
Performance Features
- Auto Minify — Minifies JS, CSS, and HTML at the edge (Speed → Optimization)
- Brotli Compression — More efficient than Gzip, enabled by default
- Early Hints — Sends 103 responses with preload links while origin processes the request
- HTTP/2 & HTTP/3 — Enabled by default on all plans
- Rocket Loader — Defers JavaScript loading to improve paint times (test carefully)
- Polish (Pro+) — Image optimization: lossless/lossy compression + WebP conversion
- Mirage (Pro+) — Lazy-loads images and serves appropriately sized versions
Cloudflare Tunnels (cloudflared)
Cloudflare Tunnels let you expose local services to the internet without opening ports on your firewall. The cloudflared daemon creates an outbound-only encrypted connection to Cloudflare's edge.
Why Use Tunnels?
- No public IP required — Works behind NAT, CGNAT, or restrictive firewalls
- No port forwarding — No firewall rules to manage
- Origin IP completely hidden — Impossible to bypass Cloudflare
- Zero Trust integration — Add authentication without modifying your app
- Free — Included on all plans including free
Installing cloudflared
# Debian/Ubuntu
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.deb
# RHEL/CentOS
curl -L --output cloudflared.rpm https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
sudo rpm -i cloudflared.rpm
# macOS
brew install cloudflare/cloudflare/cloudflared
# Docker
docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token YOUR_TOKEN
# Verify installation
cloudflared --version
Creating a Tunnel (Dashboard Method)
# 1. Dashboard: Zero Trust → Networks → Tunnels → Create a Tunnel
# 2. Name your tunnel (e.g., "my-homelab")
# 3. Choose your environment and copy the install command
# 4. Run the connector on your server:
sudo cloudflared service install YOUR_TUNNEL_TOKEN
# 5. Add a public hostname:
# Subdomain: dev
# Domain: brandonaboyd.com
# Service: http://localhost:2385
# (This creates the DNS record automatically)
# 6. The tunnel is now active — traffic flows:
# Visitor → Cloudflare Edge → Tunnel → localhost:2385
Creating a Tunnel (CLI Method)
# Authenticate
cloudflared tunnel login
# Create tunnel
cloudflared tunnel create my-site
# Configure tunnel (create config.yml)
cat > ~/.cloudflared/config.yml <<EOF
tunnel: YOUR_TUNNEL_UUID
credentials-file: /root/.cloudflared/YOUR_TUNNEL_UUID.json
ingress:
# Route dev.example.com to local web server
- hostname: dev.example.com
service: http://localhost:2385
# Route api.example.com to different service
- hostname: api.example.com
service: http://localhost:3000
# SSH access via browser
- hostname: ssh.example.com
service: ssh://localhost:22
# Catch-all (required)
- service: http_status:404
EOF
# Route DNS
cloudflared tunnel route dns my-site dev.example.com
# Run the tunnel
cloudflared tunnel run my-site
Running as a System Service
# Install as systemd service
sudo cloudflared --config /root/.cloudflared/config.yml service install
# Or if using dashboard token:
sudo cloudflared service install YOUR_TOKEN
# Manage the service
sudo systemctl status cloudflared
sudo systemctl restart cloudflared
sudo systemctl enable cloudflared # Start on boot
# View logs
sudo journalctl -u cloudflared -f
# Check tunnel status
cloudflared tunnel info my-site
Multiple Services on One Tunnel
# config.yml — route multiple subdomains through one tunnel
tunnel: abc123
credentials-file: /root/.cloudflared/abc123.json
ingress:
- hostname: www.example.com
service: http://localhost:80
originRequest:
connectTimeout: 10s
noTLSVerify: true
- hostname: dev.example.com
service: http://localhost:2385
- hostname: grafana.example.com
service: http://localhost:3000
- hostname: portainer.example.com
service: http://localhost:9000
- hostname: "*.example.com"
service: http://localhost:80
- service: http_status:404
localhost:2385, and cloudflared routes dev.brandonaboyd.com traffic to it. No ports are exposed to the public internet.
Zero Trust & Access
Cloudflare Zero Trust lets you add authentication in front of any application without modifying the app itself. Perfect for protecting admin panels, internal tools, and staging environments.
Setting Up Access Policies
# Dashboard: Zero Trust → Access → Applications → Add an Application
# 1. Self-Hosted Application
# Application domain: admin.example.com
# Session duration: 24 hours
# 2. Create an Access Policy:
# Policy name: "Team Only"
# Action: Allow
# Include: Emails ending in @yourcompany.com
#
# Or for personal use:
# Include: Email = you@gmail.com
#
# Or by IP:
# Include: IP ranges = 203.0.113.0/24
# 3. Identity Providers:
# - One-Time Pin (OTP via email) — no setup needed
# - Google (OAuth)
# - GitHub (OAuth)
# - Azure AD / Okta (SAML/OIDC)
# - Custom SAML/OIDC providers
Protect Admin Panels
# Example: Protect Portainer, Grafana, or any admin UI
# In your tunnel config:
ingress:
- hostname: portainer.example.com
service: http://localhost:9000
# In Zero Trust → Access:
# Create application for portainer.example.com
# Policy: Allow only your email via OTP
# Result: Users must verify email before seeing the app
Browser-Based SSH/VNC
# Access SSH through the browser (no SSH client needed):
# 1. Tunnel config:
ingress:
- hostname: ssh.example.com
service: ssh://localhost:22
# 2. Zero Trust → Access → Applications:
# Type: Self-Hosted
# Domain: ssh.example.com
# Policy: Allow your email
# 3. Zero Trust → Access → Applications → ssh.example.com → Settings:
# Browser Rendering: SSH
# Now visit ssh.example.com in your browser → authenticate → get a terminal
Troubleshooting Common Cloudflare Issues
Error 520: Web Server Returns Unknown Error
# Cause: Cloudflare connected to your origin, but got an unexpected response
# (empty response, oversized headers, or connection reset)
# Fix:
# 1. Check origin server is running
sudo systemctl status nginx
sudo systemctl status apache2
# 2. Check origin server logs
sudo tail -f /var/log/nginx/error.log
# 3. Verify the origin responds correctly without Cloudflare
curl -v http://localhost:80
# 4. Check response headers aren't too large (>16KB)
# 5. Ensure your server isn't rate-limiting Cloudflare IPs
Error 521: Web Server Is Down
# Cause: Cloudflare can't connect to your origin server at all
# Fix:
# 1. Verify your web server is running
sudo systemctl status nginx
# 2. Verify it's listening on the expected port
sudo ss -tlnp | grep :443
sudo ss -tlnp | grep :80
# 3. Check firewall isn't blocking Cloudflare IPs
sudo ufw status
sudo iptables -L -n
# 4. If using a tunnel, check cloudflared
sudo systemctl status cloudflared
cloudflared tunnel info my-site
Error 522: Connection Timed Out
# Cause: TCP connection to origin timed out (Cloudflare waits 15 seconds)
# Fix:
# 1. Origin server is overloaded — check CPU/memory
top
free -h
# 2. Firewall is silently dropping packets (no RST, just timeout)
sudo iptables -L -n | grep DROP
# 3. Network issues between Cloudflare and origin
mtr cloudflare-ip # from your server
# 4. Keepalive timeout too short — increase it
# Nginx: keepalive_timeout 120;
# Apache: KeepAliveTimeout 120
Error 524: A Timeout Occurred
# Cause: Cloudflare connected, but origin didn't respond within 100 seconds
# Fix:
# 1. Optimize slow backend operations
# 2. Move long-running tasks to background workers
# 3. Use Cloudflare's longer timeout (Enterprise) or restructure:
# - Return 202 Accepted immediately
# - Poll for results with AJAX/WebSocket
# 4. For uploads, increase Cloudflare's upload limit (free: 100MB)
Redirect Loops (ERR_TOO_MANY_REDIRECTS)
# Most common cause: SSL mode mismatch
# Scenario: Your origin server redirects HTTP → HTTPS,
# but Cloudflare SSL is set to "Flexible" (sends HTTP to origin)
# Result: infinite redirect loop
# Fix:
# 1. Set SSL mode to "Full" or "Full (Strict)"
# Dashboard: SSL/TLS → Overview → Full (Strict)
# 2. Or remove the HTTP→HTTPS redirect on your origin
# (let Cloudflare handle it with "Always Use HTTPS")
# 3. Check for redirect loops with:
curl -IL https://example.com
# Look for repeated 301/302 redirects
Mixed Content Warnings
# Cause: Page loaded over HTTPS but includes HTTP resources
# Fix:
# 1. Enable Automatic HTTPS Rewrites
# Dashboard: SSL/TLS → Edge Certificates → Automatic HTTPS Rewrites → ON
# This rewrites http:// URLs to https:// in HTML responses
# 2. Fix in your code — use relative or protocol-relative URLs:
# Bad: <img src="http://example.com/image.jpg">
# Good: <img src="/image.jpg">
# Good: <img src="//example.com/image.jpg">
# Best: <img src="https://example.com/image.jpg">
DNS Propagation Issues
# Check DNS resolution
dig example.com +short
nslookup example.com
# Check specific nameservers
dig example.com @ns1.cloudflare.com
dig example.com @8.8.8.8
# Check if Cloudflare is active (should return Cloudflare IPs)
dig example.com +short
# Expected: 104.x.x.x or 172.x.x.x (Cloudflare IPs)
# Not expected: Your origin IP (DNS only mode or not yet active)
# Global propagation check
# Use: https://www.whatsmydns.net/
Cloudflare Cache Issues
# Check if a resource is cached
curl -sI https://example.com/style.css | grep -i cf-cache
# cf-cache-status: HIT → Served from Cloudflare cache
# cf-cache-status: MISS → Fetched from origin
# cf-cache-status: BYPASS → Cache bypassed (dynamic content)
# cf-cache-status: EXPIRED → Cache expired, re-fetched
# cf-cache-status: DYNAMIC → Not eligible for caching
# Force cache bypass for testing
curl -H "Cache-Control: no-cache" https://example.com/style.css
# Purge specific file
# Dashboard: Caching → Configuration → Custom Purge → enter URL
# Headers to control caching from origin:
# Cache for 1 hour at Cloudflare, 5 minutes in browser:
Cache-Control: public, s-maxage=3600, max-age=300
# Never cache:
Cache-Control: no-store, no-cache, must-revalidate, private
Useful Diagnostic Headers
# Every Cloudflare response includes diagnostic headers:
curl -sI https://example.com | grep -i "cf-\|server:"
# cf-ray: 8a1b2c3d4e5f6-IAD → Unique request ID + data center
# cf-cache-status: HIT → Cache status
# server: cloudflare → Confirms traffic goes through CF
# cf-connecting-ip: (not shown) → Real visitor IP (sent to origin)
# In your origin server logs, use CF-Connecting-IP header
# to see real visitor IPs (not Cloudflare's IPs)
Quick Reference: Cloudflare Dashboard Locations
| Task | Dashboard Location |
|---|---|
| Manage DNS records | DNS → Records |
| Set SSL mode | SSL/TLS → Overview |
| Generate Origin Certificate | SSL/TLS → Origin Server |
| Enable Always HTTPS | SSL/TLS → Edge Certificates |
| Create firewall rules | Security → WAF → Custom Rules |
| Enable managed WAF | Security → WAF → Managed Rules |
| Block/allow IPs | Security → WAF → Tools |
| Configure bots | Security → Bots |
| Rate limiting | Security → WAF → Rate Limiting |
| DDoS settings | Security → DDoS |
| Under Attack Mode | Overview → Quick Actions |
| Page Rules | Rules → Page Rules |
| Redirect Rules | Rules → Redirect Rules |
| Purge cache | Caching → Configuration |
| Performance settings | Speed → Optimization |
| Cloudflare Tunnels | Zero Trust → Networks → Tunnels |
| Access policies | Zero Trust → Access → Applications |
| Analytics | Analytics & Logs → Traffic |
| Audit log | Manage Account → Audit Log |