Introduction
Cloudflare sits in front of a domain as a reverse proxy, CDN, DNS resolver, and edge firewall at the same time. Instead of visitors connecting directly to your origin server, they connect to the nearest Cloudflare data center, which terminates TLS, checks the request against firewall and WAF rules, serves cached assets when possible, and only reaches back to your origin when it actually has to.
This guide covers the parts of Cloudflare that matter for running a real site or home-lab service in front of it: DNS record management and the proxied vs. DNS-only toggle, the four SSL/TLS encryption modes and which ones are actually safe, caching and cache invalidation, the WAF and rate limiting, and Cloudflare Tunnel — a way to expose an internal service to the internet with zero open inbound ports on your firewall or router.
Who this is for
- Anyone putting a domain in front of Cloudflare for the first time.
- Sysadmins who need to stop a brute-force or scraping attack without touching the origin.
- Home-lab and self-hosting users who want to expose a service without port-forwarding.
Prerequisites
You should have the following before working through this guide:
- A registered domain name you control (access to update nameservers at the registrar).
- A free or paid Cloudflare account — the free plan covers everything in this guide.
- SSH/terminal access to your origin server, with a web server (nginx/Apache) already serving the site.
- A Cloudflare API token if you plan to automate DNS or cache purges via the API.
- For the Tunnel sections: a Linux host (VM, container, or Raspberry Pi) that can reach the internal service you want to expose.
Free plan is enough to follow along
DNS proxying, universal SSL, the core WAF managed ruleset, basic rate limiting, and Cloudflare Tunnel are all available on the free plan. Paid plans add higher rate-limit rule counts, Argo Smart Routing, and advanced bot management.Theory
What a reverse proxy / CDN actually does
A normal DNS record points a hostname straight at your server's IP — anyone who resolves the name can connect to that IP directly. A reverse proxy CDN like Cloudflare changes that: your DNS record points at Cloudflare instead, Cloudflare terminates the connection, and it decides — per request — whether to answer from cache, apply security rules, or forward the request on to your real origin. Your origin IP becomes an implementation detail the proxy hides, not something visitors talk to directly.
Anycast networking
Cloudflare announces the same IP address ranges from every data center in its network simultaneously, using anycast BGP routing. A visitor in Mumbai and a visitor in Frankfurt both resolve the same Cloudflare IP, but internet routing sends each of them to whichever announcing data center is topologically closest. This is what makes Cloudflare fast globally (short round trips to the edge) and resilient (a data center going dark just drops out of the anycast announcement, and traffic reroutes to the next-closest PoP with no visible failover).
Where Cloudflare sits
| Layer | What happens here |
|---|---|
| DNS | Cloudflare's authoritative nameservers answer queries for your zone. |
| TLS termination | The visitor's HTTPS connection ends at the Cloudflare edge, not your origin. |
| Firewall / WAF | Requests are matched against custom rules and managed rulesets before anything reaches your origin. |
| Cache | Static and rule-matched content is served directly from edge storage. |
| Origin connection | Only cache misses and dynamic requests are proxied back to your real server, ideally over a second, separate TLS connection. |
Two separate TLS connections, not one
When a record is proxied, there are two independent HTTPS connections: visitor ↔ Cloudflare, and Cloudflare ↔ origin. The SSL/TLS encryption mode (covered in Configuration) controls whether — and how strictly — that second connection is encrypted and verified.Architecture
The request path below shows a proxied request from browser to origin and back, including the WAF, rate limiting, and cache decision points at the edge:
Visitor → Cloudflare anycast DNS → nearest edge PoP → WAF/rate limit → cache → origin
Cache misses still benefit from the edge
Even on a full cache miss, the visitor's TCP/TLS handshake only ever reaches the nearby Cloudflare PoP. The PoP-to-origin leg can reuse a persistent, already-warm connection (via Cloudflare's backbone), which is usually faster than the visitor's client negotiating TLS directly with a distant origin.Installation
"Installing" Cloudflare means onboarding your domain so its DNS is served (and optionally proxied) by Cloudflare. There are two supported paths depending on whether you can change nameservers at your registrar.
The standard path — Cloudflare becomes the authoritative DNS provider for the entire domain.
1. Dashboard → Add a Site → enter your domain (e.g. example.com)
2. Choose a plan (Free is fine to start)
3. Cloudflare scans existing DNS records and imports them automatically
4. Review the imported records — fix anything the scan missed or got wrong
5. Cloudflare gives you two nameservers, e.g.:
ada.ns.cloudflare.com
rex.ns.cloudflare.com
6. Log in to your domain registrar and replace the existing nameservers
with the two Cloudflare ones
7. Wait for propagation (usually minutes, can take up to 24h) — Cloudflare
emails you once the zone is activeDouble-check every imported record
The automatic scan is good but not perfect, especially for MX records, SPF/DKIM TXT records, and subdomains pointed at third-party services. Verify mail and any external integrations before cutting over nameservers, or mail delivery can silently break.Configuration
DNS record types
| Type | Purpose | Can be proxied? |
|---|---|---|
A | Hostname → IPv4 address | Yes |
AAAA | Hostname → IPv6 address | Yes |
CNAME | Hostname → another hostname | Yes |
MX | Mail server for the domain | No — always DNS-only |
TXT | Arbitrary text — SPF, DKIM, domain verification | No — always DNS-only |
Proxied (orange cloud) vs. DNS-only (grey cloud)
Every A/AAAA/CNAME record has a proxy toggle in the dashboard. Proxied (orange cloud) routes traffic through Cloudflare's edge — you get caching, WAF, DDoS protection, and your origin IP is hidden. DNS-only(grey cloud) just answers the DNS query with the real IP; the visitor connects straight to your server with none of Cloudflare's protections. Records that need a direct connection — SSH, a mail server, or anything not speaking HTTP/HTTPS — must stay DNS-only, since Cloudflare's proxy only understands HTTP(S) and a handful of other protocols on paid plans.
SSL/TLS encryption modes
This is the single most misconfigured setting in Cloudflare. It controls how the Cloudflare → origin leg of the connection is encrypted — it has no effect on the visitor → Cloudflare leg, which is always HTTPS once Universal SSL is active.
| Mode | Origin connection | Safe to use? |
|---|---|---|
| Off | Plaintext HTTP to the origin, and Cloudflare won't serve HTTPS to visitors either. | No — never use in production. |
| Flexible | Visitor↔Cloudflare is HTTPS, but Cloudflare↔origin is plain HTTP. | Only if the origin genuinely cannot do TLS. Causes redirect loops if the origin app also redirects HTTP→HTTPS. |
| Full | Cloudflare↔origin is HTTPS, but any certificate (including self-signed) is accepted without validation. | Better than Flexible, but still vulnerable to interception between edge and origin. |
| Full (Strict) | Cloudflare↔origin is HTTPS and the origin certificate must be valid and trusted (a Cloudflare Origin CA cert, or a public CA cert). | Yes — this is the only mode that is actually end-to-end encrypted and verified. |
Flexible + an app that redirects to HTTPS = infinite redirect loop
If SSL/TLS mode is Flexible and your origin app forces HTTP→HTTPS redirects, the origin sees plain HTTP from Cloudflare, redirects to HTTPS, Cloudflare fetches over HTTP again, and the cycle repeats until the browser gives up withERR_TOO_MANY_REDIRECTS. The fix is almost always to switch to Full or Full Strict, not to disable the origin's redirect.Origin CA certificates
For Full Strict mode, Cloudflare can issue a free Origin Certificate that's trusted by Cloudflare's edge (though not by regular browsers directly hitting the origin) and valid for up to 15 years:
# Dashboard → SSL/TLS → Origin Server → Create Certificate
# generates a cert + private key pair; save them on the origin as:
sudo mkdir -p /etc/ssl/cloudflare
sudo nano /etc/ssl/cloudflare/cert.pem # paste the Origin Certificate
sudo nano /etc/ssl/cloudflare/key.pem # paste the private key
sudo chmod 600 /etc/ssl/cloudflare/key.pemserver {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/cloudflare/cert.pem;
ssl_certificate_key /etc/ssl/cloudflare/key.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-Proto https;
}
}Caching
Cloudflare caches based on file extension by default (images, CSS, JS) at the Standard cache level. Browser Cache TTLcontrols how long the visitor's own browser is told to keep a cached copy, separate from how long Cloudflare's edge keeps it. For anything not cached by default — API responses, HTML pages — use Cache Rules (the modern replacement for legacy Page Rules) to opt specific paths into caching:
Rule name: Cache static assets aggressively
When incoming requests match:
URI Path contains "/static/" OR URI Path ending in .js, .css, .woff2, .jpg, .png
Then:
Cache eligibility: Eligible for cache
Edge TTL: 30 days
Browser TTL: 4 hoursAfter a deploy, stale cached assets are cleared from Caching → Configuration → Purge Cache — either purge everything, or purge by exact URL to avoid a full cold-cache stampede on the origin.
Commands
cloudflared — the Cloudflare CLI / tunnel daemon
# Install (Debian/Ubuntu)
curl -L https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared
# Authenticate this machine to your Cloudflare account
cloudflared tunnel login
# Create a named tunnel (generates a credentials JSON file)
cloudflared tunnel create home-lab
# List tunnels and their IDs
cloudflared tunnel list
# Route a hostname to the tunnel (creates the DNS CNAME automatically)
cloudflared tunnel route dns home-lab app.example.com
# Run the tunnel in the foreground (test) or as a system service
cloudflared tunnel run home-lab
sudo cloudflared service install
sudo systemctl enable --now cloudflaredCloudflare API via curl
Almost everything in the dashboard has an API equivalent, authenticated with a scoped API token (Profile → API Tokens) rather than the old global API key:
export CF_TOKEN="your-scoped-api-token"
export ZONE_ID="your-zone-id"
# List zones on the account
curl -s -X GET "https://api.cloudflare.com/client/v4/zones" \
-H "Authorization: Bearer $CF_TOKEN" | jq
# Create a proxied A record
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/json" \
--data '{"type":"A","name":"app","content":"203.0.113.10","proxied":true,"ttl":1}'
# Purge the entire cache for a zone
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'
# Purge specific URLs only (avoids a full cold-cache stampede)
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/json" \
--data '{"files":["https://example.com/static/app.js"]}'
# Set the SSL/TLS mode to Full (strict)
curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/settings/ssl" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/json" \
--data '{"value":"strict"}'Examples
Exposing an internal service (e.g. a home-lab dashboard on 192.168.1.50:8080) to the internet through Cloudflare Tunnel, with no inbound firewall or router rules at all:
Authenticate cloudflared to your account
cloudflared tunnel loginopens a browser to authorize the CLI against your Cloudflare account and zone, storing a certificate locally under~/.cloudflared/.Create a named tunnel
cloudflared tunnel create homelab-dashboardregisters the tunnel and writes a credentials JSON file — this is the tunnel's identity, not the service itself.Write the tunnel config
A YAML file maps public hostnames to internal services — this is where the "which service does this hostname reach" logic lives, entirely on the tunnel host, never exposed as an open port.Route DNS and start the tunnel
cloudflared tunnel route dns homelab-dashboard dash.example.comcreates a proxied CNAME pointing at the tunnel automatically, thencloudflared tunnel run homelab-dashboardopens four persistent outbound QUIC connections to the Cloudflare edge — outbound only, so nothing needs to be forwarded on the router.Run it as a system service
sudo cloudflared service installfollowed bysystemctl enable --now cloudflaredkeeps the tunnel alive across reboots, reconnecting automatically if the network drops.
tunnel: homelab-dashboard
credentials-file: /home/deploy/.cloudflared/<tunnel-id>.json
ingress:
- hostname: dash.example.com
service: http://192.168.1.50:8080
- hostname: git.example.com
service: http://192.168.1.51:3000
- service: http_status:404Zero open inbound ports
The router/firewall on this network never needs a single port-forward rule. The tunnel host dials out to Cloudflare over 443/QUIC; Cloudflare proxies inbound visitor requests down that same outbound connection. There is nothing for an attacker to port-scan on the public IP for this service.Real World Example
Scenario: a marketing blog post goes viral and, within the same hour, login-endpoint traffic spikes abnormally — a mix of legitimate readers and a credential-stuffing bot run trying to exploit the sudden attention.
Confirm what's actually happening
Security → Analytics shows a spike in requests to/wp-login.phpfrom a wide, unrelated spread of IPs — not the shape of organic traffic. Traffic to the actual blog post is separately spiking too, so this isn't purely an attack; it's real load plus an opportunistic bot.Rate-limit the sensitive endpoint immediately
Add a Rate Limiting rule: ifURI Path equals /wp-login.phpand requests from the same IP exceed 5 in 1 minute, respond with a 1-hour block. This stops the credential-stuffing pattern without touching normal readers, who never hit that path at that rate.Offload the legitimate spike with caching
The viral blog post itself is static HTML — add a Cache Rule to cache it at the edge with a short edge TTL (e.g. 5 minutes), so repeat hits from thousands of readers are served from Cloudflare's cache instead of regenerating the page on the origin every time.Raise the Security Level as a stopgap
Temporarily raise Security Level to "High" (or enable "I'm Under Attack Mode" if the origin starts struggling), which adds a JavaScript/managed challenge for visitors with lower reputation scores while the rate limit rule takes effect.Review and tune after the fact
Once traffic normalizes, check the WAF event log for any legitimate requests that got challenged, add a narrow exception if needed, and turn Security Level back down — leaving the rate limit rule on the login path permanently.
Common Issues
ERR_TOO_MANY_REDIRECTS right after enabling Cloudflare
Almost always caused by SSL/TLS mode set to Flexible while the origin app also forces an HTTP→HTTPS redirect. Cloudflare talks HTTP to the origin in Flexible mode, the origin redirects to HTTPS, and the loop repeats forever. Switch the mode to Full or Full (Strict) once the origin actually serves HTTPS.522 Connection Timed Out / 524 A Timeout Occurred
A 522 means Cloudflare couldn't establish a TCP connection to the origin at all — check that the origin firewall allows Cloudflare's IP ranges on the proxied port, and that the web server is actually listening there. A 524 means the connection opened but the origin took longer than 100 seconds to respond — look at slow database queries or upstream timeouts on the origin itself.Old content still showing after a deploy
Browsers, Cloudflare's edge cache, and any CDN in between can all be holding a stale copy. Purge the affected URLs (or purge everything) from Caching → Configuration, and set sensibleCache-Controlheaders on the origin response so future deploys don't need a manual purge at all.DNS changes not taking effect
If a record is proxied,dig/nslookupwill correctly show a Cloudflare IP, not your origin's — that's expected, not a propagation failure. Genuine propagation delay only applies right after switching nameservers at the registrar; check status with dig NS example.com and be patient with TTLs cached by resolvers upstream.WAF blocking legitimate traffic (false positive)
Check Security → Events for the specific rule ID that matched. Rather than disabling the whole managed ruleset, add a narrow WAF exception scoped to the specific path or parameter that trips the rule (e.g. a CMS admin field that legitimately contains HTML-like text), keeping the rest of the ruleset active.Best Practices
- Run SSL/TLS in Full (Strict) with a Cloudflare Origin Certificate — never Flexible or Off in production.
- Enable "Always Use HTTPS" and HSTS once every subdomain genuinely supports HTTPS, so nothing accidentally falls back to plaintext.
- Use Cache Rules for static assets and purge by specific URL after deploys instead of purging everything, to avoid a cold-cache stampede on the origin.
- Automate DNS and cache-purge operations with scoped API tokens instead of the account-wide global API key.
- Prefer Cloudflare Tunnel over exposed origin IPs and port-forwarding wherever the workload allows it — it removes an entire class of direct-to-origin attacks.
Security Hardening
- Full (Strict) SSL only — anything less leaves the Cloudflare-to-origin leg unencrypted or unverified.
- WAF managed rules — keep the Cloudflare Managed Ruleset and OWASP Core Ruleset enabled; they cover the common injection, XSS, and protocol-abuse patterns without custom rule authoring.
- Bot Fight Mode — free-tier bot mitigation that challenges known bad bot signatures before they reach the origin.
- Hide the origin IP — proxy every record that serves web traffic, avoid leaking the real IP in old DNS history/subdomains, and restrict the origin firewall to Cloudflare's published IP ranges only.
- Firewall rules for admin paths — a custom rule like
(http.request.uri.path contains "/wp-admin" and not ip.src in {office IP range})blocks admin panel access from anywhere but trusted networks.
Interview Questions
Cheat Sheet
# --- cloudflared / Tunnel ---
cloudflared tunnel login
cloudflared tunnel create <name>
cloudflared tunnel list
cloudflared tunnel route dns <name> <hostname>
cloudflared tunnel run <name>
sudo cloudflared service install && sudo systemctl enable --now cloudflared
cloudflared tunnel info <name>
cloudflared tunnel delete <name>
# --- API auth ---
export CF_TOKEN="..."; export ZONE_ID="..."
# --- DNS ---
curl -s https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records \
-H "Authorization: Bearer $CF_TOKEN"
curl -s -X POST https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records \
-H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
--data '{"type":"A","name":"app","content":"203.0.113.10","proxied":true,"ttl":1}'
# --- Cache ---
curl -s -X POST https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache \
-H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
--data '{"purge_everything":true}'
# --- SSL mode ---
curl -s -X PATCH https://api.cloudflare.com/client/v4/zones/$ZONE_ID/settings/ssl \
-H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
--data '{"value":"strict"}'
# --- Security level ---
curl -s -X PATCH https://api.cloudflare.com/client/v4/zones/$ZONE_ID/settings/security_level \
-H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" \
--data '{"value":"under_attack"}'Summary
Putting Cloudflare in front of a site means treating the edge as a real part of your infrastructure, not just a DNS host. Get the fundamentals right — proxied records where they belong, Full (Strict) SSL with an Origin Certificate, sensible caching with a real purge workflow, and WAF/rate limiting rules scoped to what actually needs protecting — and you get a CDN, a firewall, and a DDoS shield for free, plus Cloudflare Tunnel as a way to expose internal services without ever opening an inbound port. Most production incidents trace back to one of a small number of causes: the wrong SSL mode, a stale cache, or a WAF rule that was never scoped narrowly enough — all of which are covered in the Troubleshooting section above.
Resources
- Official Cloudflare documentation —
developers.cloudflare.com - Cloudflare engineering blog —
blog.cloudflare.com - Cloudflare community forums —
community.cloudflare.com - cloudflared source and releases —
github.com/cloudflare/cloudflared - Cloudflare Radar (global internet traffic and attack trends) —
radar.cloudflare.com - Cloudflare API reference —
api.cloudflare.com