Skip to content

Cloudflare: DNS, SSL, Caching, WAF & Tunnels

Put Cloudflare in front of anything — correct SSL modes, caching strategy, a real WAF ruleset, and zero-open-port access via Tunnel.

CloudflareDNSWAFCloudflare Tunnel
RR

Ranjith R

Linux SysAdmin & Cloud Engineer

Published January 28, 2026Updated June 30, 2026
10 min readIntermediate#Cloudflare#DNS#CDN#Security#WAF
Share
Section 01

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.
Section 02

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.
Section 03

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

LayerWhat happens here
DNSCloudflare's authoritative nameservers answer queries for your zone.
TLS terminationThe visitor's HTTPS connection ends at the Cloudflare edge, not your origin.
Firewall / WAFRequests are matched against custom rules and managed rulesets before anything reaches your origin.
CacheStatic and rule-matched content is served directly from edge storage.
Origin connectionOnly 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.
Section 04

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.
Section 05

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.

steps
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 active

Double-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.
Section 06

Configuration

DNS record types

TypePurposeCan be proxied?
AHostname → IPv4 addressYes
AAAAHostname → IPv6 addressYes
CNAMEHostname → another hostnameYes
MXMail server for the domainNo — always DNS-only
TXTArbitrary text — SPF, DKIM, domain verificationNo — 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.

ModeOrigin connectionSafe to use?
OffPlaintext HTTP to the origin, and Cloudflare won't serve HTTPS to visitors either.No — never use in production.
FlexibleVisitor↔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.
FullCloudflare↔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 with ERR_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:

terminal (on the origin server)
# 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.pem
/etc/nginx/sites-available/example.com
server {
    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:

Cache Rule example
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 hours

After 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.

Section 07

Commands

cloudflared — the Cloudflare CLI / tunnel daemon

terminal
# 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 cloudflared

Cloudflare 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:

terminal
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"}'
Section 08

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:

  1. Authenticate cloudflared to your account

    cloudflared tunnel login opens a browser to authorize the CLI against your Cloudflare account and zone, storing a certificate locally under ~/.cloudflared/.
  2. 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.
  3. 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.
  4. Route DNS and start the tunnel

    cloudflared tunnel route dns homelab-dashboard dash.example.com creates a proxied CNAME pointing at the tunnel automatically, then cloudflared tunnel run homelab-dashboard opens four persistent outbound QUIC connections to the Cloudflare edge — outbound only, so nothing needs to be forwarded on the router.
  5. Run it as a system service

    sudo cloudflared service install followed by systemctl enable --now cloudflared keeps the tunnel alive across reboots, reconnecting automatically if the network drops.
~/.cloudflared/config.yml
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:404

Zero 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.
Section 09

Screenshots

Cloudflare DNS dashboard — proxied (orange cloud) vs. DNS-only (grey cloud) records

Screenshot placeholder

Security → WAF event log showing a blocked request with matched rule and rule ID

Screenshot placeholder

Section 10

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.

  1. Confirm what's actually happening

    Security → Analytics shows a spike in requests to /wp-login.php from 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.
  2. Rate-limit the sensitive endpoint immediately

    Add a Rate Limiting rule: if URI Path equals /wp-login.php and 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.
  3. 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.
  4. 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.
  5. 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.
Troubleshooting

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 sensible Cache-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

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

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

Interview Questions

A proxied (orange cloud) record routes traffic through Cloudflare's edge, hiding the origin IP and enabling caching, WAF, and DDoS protection. A DNS-only (grey cloud) record just resolves to the real IP — the visitor connects directly to the origin with none of Cloudflare's protections in the path.
Cheat Sheet

Cheat Sheet

cloudflare-cheatsheet.sh
# --- 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

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

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