In 30 seconds
Cloudflare sits between your visitors and your server. It hosts your DNS, and for proxied records it terminates HTTPS at the nearest of its edge locations, caches files, filters attacks and forwards the rest to your origin. Set up correctly, a site gets faster and much harder to take down. Set up wrongly, you get redirect loops and 52x errors, so the details below matter.
Why I use it
- I use Cloudflare in front of sites for DNS and basic DDoS protection; it is part of my security baseline for every server.
- Its free plan gives a CDN, HTTPS at the edge, a WAF and HTTP/3, which is a lot of value for small and mid-size projects.
- Hiding the origin IP behind the proxy and only allowing traffic on ports 80 and 443 shrinks the attack surface.
- My plan is to keep DNS for all domains on Cloudflare so every site is managed and protected the same way.
How it works
01Step by step: connecting a domain
1) Create a Cloudflare account with two-factor authentication and add your site (the root domain, for example example.com). Choose a plan; Free is fine to start. 2) Cloudflare scans your current DNS and imports the records it finds. Compare them carefully with your current zone, because the scan can miss records: check A, AAAA, CNAME, MX, every TXT (SPF, DKIM selectors, DMARC, verifications) and SRV. 3) Cloudflare gives you two nameservers. At your registrar, replace the existing nameservers with those two, and turn off DNSSEC at the registrar first if it is on (re-enable it through Cloudflare later). 4) Wait until the zone shows Active; usually minutes, up to 24 hours. 5) Prepare the origin certificate and SSL mode before enabling the proxy on important records (next sections). 6) Verify with dig and curl.
# Nameservers switched?
dig example.com NS +short
# Proxied records return Cloudflare IPs, not your server's
dig example.com A +short
# Response passes through Cloudflare
curl -sI https://example.com/ | grep -iE "server|cf-ray|cf-cache-status"
02Proxied (orange cloud) vs DNS only (grey cloud)
Each A, AAAA and CNAME record has a proxy status. Proxied means DNS answers with Cloudflare's IPs; visitors connect to Cloudflare, which then connects to your origin. You get caching, WAF, DDoS protection and a hidden origin IP. DNS only means Cloudflare just answers with your real IP, like any DNS host, and none of the protection applies. Proxy your website hostnames (example.com, www). Keep DNS only for things that are not HTTP or must reach the server directly: mail hosts used in MX records, SSH or database hostnames, and services on ports Cloudflare does not proxy. Remember that a single DNS-only record pointing at the same server reveals the origin IP, so do not create one for the web server's address unless you need it.
- Proxy: web traffic on standard HTTP(S) ports.
- DNS only: mail, SSH, FTP, game or custom TCP/UDP services.
- MX records cannot point at a proxied hostname.
03SSL/TLS modes: why Full (strict)
There are two connections: visitor to Cloudflare, and Cloudflare to your origin. The SSL/TLS encryption mode controls the second one. Off: no HTTPS at all. Flexible: HTTPS to the visitor, plain HTTP to your server; the padlock shows, but traffic to the origin is unencrypted, and an origin that redirects HTTP to HTTPS creates an endless loop. Full: HTTPS to the origin, but the certificate is not checked, so a self-signed or expired one passes and a man in the middle could too. Full (strict): HTTPS to the origin and the certificate must be valid and match the hostname, issued by a public CA or by Cloudflare's Origin CA. Always aim for Full (strict). Install either a Let's Encrypt certificate (certbot) or a free Cloudflare Origin Certificate on the server, then switch the mode. Cloudflare also offers an Automatic mode that picks the strongest mode your origin supports, but setting Full (strict) explicitly is clearer.
# nginx on the origin with a Cloudflare Origin Certificate
server {
listen 443 ssl;
http2 on;
server_name example.com www.example.com;
ssl_certificate /etc/ssl/cloudflare/example.com.pem;
ssl_certificate_key /etc/ssl/cloudflare/example.com.key;
root /var/www/example/public;
}
04HTTPS everywhere, caching and performance
Under SSL/TLS > Edge Certificates turn on Always Use HTTPS, which redirects every http:// request at the edge, and set Minimum TLS Version to 1.2. Enable HSTS only after the whole site and every subdomain you include work on HTTPS, starting with a short max-age; once browsers cache it, they refuse plain HTTP. By default Cloudflare caches static files by extension (images, CSS, JS, fonts) and does not cache HTML. Cache Rules let you change that: cache public HTML for a short time, bypass anything with a session cookie, /admin, /api and /checkout, and set Edge and Browser TTLs. The cf-cache-status header shows HIT, MISS, BYPASS or DYNAMIC. Purge by URL after deploys, or better, use versioned asset filenames so purges are rarely needed. HTTP/3 and Brotli are on by default on current plans.
- Never cache pages that depend on cookies or login state.
- Use Development Mode or purge to test changes; do not disable caching globally.
- Avoid Rocket Loader and HTML minification tricks that can break JavaScript apps; test first.
05Security: WAF, rate limiting, DDoS, locking the origin
DDoS protection for network and HTTP floods is automatic on every plan for proxied traffic. The managed WAF ruleset (with broader rulesets on paid plans) blocks common attacks such as SQL injection patterns. Custom rules let you block or challenge by country, path, user agent or bot score; for example, a managed challenge on /wp-login.php or /admin from outside expected countries. Rate limiting rules cap requests per IP to sensitive endpoints such as login, OTP and password reset. Bot Fight Mode adds basic bot protection. The proxy only helps if attackers cannot reach the origin directly: firewall the server so ports 80 and 443 accept only Cloudflare's published IP ranges (or use Cloudflare Tunnel, which needs no open inbound ports), and keep SSH limited to keys and trusted IPs. Under Attack Mode is an emergency switch that challenges every visitor.
# ufw: allow web traffic only from Cloudflare (ranges from cloudflare.com/ips)
for ip in $(curl -s https://www.cloudflare.com/ips-v4) $(curl -s https://www.cloudflare.com/ips-v6); do
sudo ufw allow proto tcp from "$ip" to any port 80,443
done
sudo ufw status numbered
06Real visitor IP on the origin
Behind the proxy, every request reaches your server from a Cloudflare IP. Logs, rate limiting, fail2ban and fraud checks then see Cloudflare instead of the visitor and can even ban Cloudflare itself. Cloudflare sends the original address in the CF-Connecting-IP header (and in X-Forwarded-For). Tell nginx to trust that header only when the request comes from Cloudflare ranges, using the realip module. Then $remote_addr becomes the visitor's IP everywhere. In Laravel or Node apps behind nginx, also configure trusted proxies so the framework reads the correct client IP and scheme. Never trust the header from arbitrary sources, or anyone can fake their IP.
# /etc/nginx/conf.d/cloudflare-realip.conf (list all current ranges)
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 2400:cb00::/32;
# ... remaining ranges from https://www.cloudflare.com/ips/
real_ip_header CF-Connecting-IP;
07Troubleshooting 52x errors and redirect loops
When Cloudflare shows an error page, the problem is almost always between Cloudflare and your origin. 521 Web server is down: the origin refused the connection; nginx stopped, or a firewall blocks Cloudflare IPs. 522 Connection timed out: Cloudflare could not complete a TCP connection; server overloaded, wrong IP in DNS, or packets dropped by a firewall. 524: the connection worked but the app took longer than about 125 seconds (the default proxy timeout) to respond; move long work to queues. 525 SSL handshake failed: the origin does not speak TLS on 443 or has no certificate for that hostname (SNI). 526 Invalid SSL certificate: in Full (strict) the origin certificate is expired, self-signed or for another name. Too many redirects: usually Flexible mode plus an origin that redirects HTTP to HTTPS; switch to Full (strict). Check origin logs and test the origin directly.
# Test the origin directly, bypassing Cloudflare
curl -svI https://example.com/ --resolve example.com:443:203.0.113.10 2>&1 | grep -E "HTTP/|subject|expire|issuer"
sudo systemctl status nginx
sudo tail -n 50 /var/log/nginx/error.log
Use it when
- Almost any public website or API that benefits from free DNS, HTTPS, CDN and DDoS protection.
- Hiding a single VPS origin from direct attacks.
- Sites with visitors far from the server, where edge caching cuts latency.
- Quickly blocking abusive traffic with WAF and rate limiting rules.
Skip it when
- Non-HTTP services (mail, SSH, databases) on the free proxy; keep them DNS only or use dedicated products.
- When compliance or data residency rules forbid a third party terminating TLS, unless you use the matching enterprise features.
- Very large uploads or long-running requests that hit plan limits (upload size, about 125 s proxy timeout).
Alternatives
- AWS CloudFront + Route 53 + WAF
Good fit inside AWS, more configuration and pay-per-use pricing.
- Fastly
Powerful programmable edge, aimed at larger teams.
- Bunny.net
Simple, low-cost CDN; less security tooling.
- No CDN
Fine for internal tools; you handle certificates and DDoS exposure yourself.
Best practices
- Use SSL mode Full (strict) with an Origin CA or Let's Encrypt certificate on the server.
- Turn on Always Use HTTPS and minimum TLS 1.2; add HSTS only after verifying all subdomains.
- Firewall the origin to Cloudflare IP ranges or use Cloudflare Tunnel.
- Restore the real visitor IP with the nginx realip module and trusted proxies in the app.
- Write explicit Cache Rules and bypass anything personalized.
- Rate-limit login, OTP and password reset endpoints.
- Protect the Cloudflare account with 2FA and scoped API tokens instead of the global key.
Common mistakes
- Using Flexible mode, then adding an HTTP to HTTPS redirect on the origin and getting a redirect loop.
- Leaving a DNS-only record that exposes the origin IP of the proxied web server.
- Importing DNS and missing MX or DKIM records, breaking email after the nameserver switch.
- Caching HTML for logged-in users, so one user sees another user's page.
- Forgetting the real IP setup, so fail2ban bans Cloudflare's IP addresses.
Deep dive
08What happens to a request inside Cloudflare
When a visitor connects to a proxied hostname, anycast routes them to the nearest Cloudflare data center, where TLS ends with the edge certificate. The request then passes a series of phases in a fixed order. Network and HTTP DDoS mitigation come first. Then security rules: IP access rules, WAF custom rules, rate limiting rules and the managed WAF rulesets, plus bot checks. Configuration rules, redirect rules and URL rewrites adjust the request. Workers, if attached to the route, run next and can answer or modify the request. Only then does the cache look up the cache key. On a miss, Origin Rules can change the host, port or SNI, and the request goes to your origin, possibly through a regional tier first. Knowing this order explains things like a WAF rule blocking a request that would have been a cache hit anyway.
- Security runs before cache, so blocked requests never consume cache or origin.
- Use Trace in the dashboard to see which rules matched a given URL.
- Workers run before the cache; use the Cache API inside a Worker to cache its output.
09Cache keys, tiered cache and origin cache headers
The cache key decides what counts as the same object. By default it includes the scheme, host, path and full query string, so ?utm_source=x creates a separate copy; a Cache Rule can ignore or sort query strings, or add a header or cookie to the key. Cloudflare respects origin Cache-Control by default for cacheable content: s-maxage and max-age set the edge lifetime, private and no-store prevent storage, and Set-Cookie on a response usually stops it being cached. Edge TTL in a Cache Rule can override origin headers, which is powerful and dangerous. Tiered Cache makes lower-tier data centers ask an upper tier instead of your origin, which raises hit ratio and protects a small VPS. Purge options include single URL, everything, and on some plans tag, prefix or hostname purges.
# Let the origin control edge vs browser caching
location /blog/ {
add_header Cache-Control "public, max-age=60, s-maxage=600, stale-while-revalidate=300";
}
location /account/ {
add_header Cache-Control "private, no-store";
}
# Check: curl -sI https://example.com/blog/ | grep -iE "cf-cache-status|age|cache-control"
10Locking the origin: Authenticated Origin Pulls and Tunnel
Firewalling to Cloudflare IP ranges stops random scanners, but any Cloudflare customer's traffic also comes from those ranges. Two stronger options exist. Authenticated Origin Pulls use mutual TLS: Cloudflare presents a client certificate when connecting to your origin, and nginx refuses any TLS connection without it. Zone-level pulls use a Cloudflare-issued certificate; per-hostname pulls let you upload your own, which is stricter because only your zone uses it. Cloudflare Tunnel goes further: a small daemon, cloudflared, runs on your server and opens outbound connections to Cloudflare, so the server needs no public inbound web ports at all. Tunnel also works for internal tools when combined with Cloudflare Access, which puts login in front of an app. The trade-off is one more agent to monitor and update.
# nginx: accept only connections that present Cloudflare's client cert
ssl_client_certificate /etc/ssl/cloudflare/authenticated_origin_pull_ca.pem;
ssl_verify_client on;
# Or: no open ports, with Cloudflare Tunnel
cloudflared tunnel login
cloudflared tunnel create web-prod
cloudflared tunnel route dns web-prod app.example.com
cloudflared tunnel run web-prod
11Limits and edge cases to plan for
A few platform limits shape architecture. Request body size for uploads through the proxy is capped per plan (100 MB on Free and Pro), so large uploads should go directly to object storage with presigned URLs. The proxy read timeout produces a 524 when the origin is silent too long; current documentation gives a default of about two minutes, raisable only on Enterprise, so long reports and imports belong in background jobs with polling or webhooks. WebSockets are supported but long-lived connections can be reset during Cloudflare maintenance, so clients must reconnect. Only certain ports are proxied (for HTTPS, for example 443, 2053, 2083, 2087, 2096 and 8443). The free Universal certificate covers the apex and one level of subdomain, so deeper names like a.b.example.com need an advanced certificate. Check current plan limits before committing to a design.
Real-world scenarios
Login is under a credential-stuffing attack
Problem
Thousands of login attempts per minute arrive from rotating IPs. The app slows down and real users get locked out by the app's own lockout rules.
How to solve it
Add a rate limiting rule on the login path, for example POST /login, counting by IP with a low threshold per minute, and respond with a managed challenge rather than a hard block so real users can pass. Add a WAF custom rule that challenges requests to the login path with a low bot score or from countries you do not serve. Confirm the origin sees real IPs through the realip module, so app-level limits target attackers and not Cloudflare. In the app, prefer progressive delays over account lockout, and encourage two-factor authentication. Watch Security Events to tune thresholds.
A report export fails with 524
Problem
Admins export a large CSV. After about two minutes Cloudflare shows a 524 page, although the origin logs show the job eventually completes.
How to solve it
The origin did not send a response before the proxy read timeout. Raising it is only possible on Enterprise, so change the design: the export request creates a queued job and returns 202 Accepted with a job ID. A worker builds the file and uploads it to object storage. The frontend polls a status endpoint or receives a notification, then downloads from a signed URL. As a short-term workaround you can serve the admin hostname as DNS only, but you lose protection, so lock it down by IP or put it behind Cloudflare Access.
Users see each other's dashboards
Problem
After someone added a "Cache Everything" rule to speed up the site, a customer reports seeing another customer's name on their account page.
How to solve it
Treat it as a data leak. Remove or narrow the rule immediately and purge everything. Then design caching explicitly: a Cache Rule that caches only public paths, and a bypass when a session cookie is present or the path is /account, /admin, /api or /checkout. On the origin, send Cache-Control: private, no-store on personalised responses, so even a broad rule does not store them if it respects origin headers. Verify with curl that cf-cache-status shows BYPASS or DYNAMIC on private pages. Review logs to see which users were affected.
Cheat sheet
- Is the response served through Cloudflare?
curl -sI https://example.com/ | grep -iE "server|cf-ray"- Was it a cache hit?
curl -sI https://example.com/app.css | grep -i cf-cache-status- Bypass Cloudflare to test the origin
curl -svI https://example.com/ --resolve example.com:443:203.0.113.10- Current Cloudflare IPv4 ranges
curl -s https://www.cloudflare.com/ips-v4- Which data center served me
curl -s https://example.com/cdn-cgi/trace- Purge one URL via API
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" -H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: application/json" --data '{"files":["https://example.com/app.css"]}'- Recommended SSL/TLS mode
SSL/TLS > Overview > Full (strict)- Real IP header for nginx
real_ip_header CF-Connecting-IP;- Expose a local service without open ports
cloudflared tunnel run web-prod
In my real projects
- Cloudflare is in use for DNS on some of the sites I manage, and "Cloudflare in front for DNS and basic DDoS protection" is part of my server security baseline.
- My baseline also keeps only ports 22, 80 and 443 open and HTTPS everywhere with auto-renewing certificates, which pairs with the proxy.
- My consolidation plan moves DNS for all domains to Cloudflare, switching traffic by DNS only after the new server is verified.
Questions & answers
Q1What is the difference between Flexible, Full and Full (strict)?
All three use HTTPS between the visitor and Cloudflare. Flexible then talks plain HTTP to the origin, so data is unencrypted on the second leg and redirect loops are common. Full uses HTTPS to the origin but accepts any certificate, including self-signed or expired ones, so it encrypts but does not authenticate the server. Full (strict) encrypts and validates the certificate against a public CA or Cloudflare's Origin CA, and checks the hostname. Only Full (strict) gives real end-to-end security. I install an Origin Certificate or Let's Encrypt certificate on the server and set Full (strict) from day one.
Q2A site behind Cloudflare shows error 522. How do you debug it?
522 means Cloudflare could not open a TCP connection to the origin in time, so I look at the path to the server, not the app. First I confirm the DNS record points to the correct origin IP. Then I check that the server is up and not overloaded, that nginx listens on 80 and 443, and that the firewall, both ufw and the provider's firewall, allows Cloudflare's IP ranges. I test the origin directly with curl --resolve. I also check fail2ban or rate limits that might have blocked Cloudflare IPs because the real IP was not restored. Origin logs confirm whether requests arrive at all.
Q3Why do I get "too many redirects" after enabling Cloudflare?
Almost always it is Flexible SSL mode combined with an HTTPS redirect on the origin. The visitor requests HTTPS, Cloudflare forwards it as plain HTTP, the origin redirects to HTTPS, Cloudflare forwards again as HTTP, and the loop never ends. The fix is to install a certificate on the origin and switch the mode to Full (strict). Other causes are conflicting redirect rules between Cloudflare and the app, or an app that does not trust X-Forwarded-Proto and thinks every request is HTTP. In that case configure trusted proxies so the framework sees the original scheme.
Q4How do you see the real visitor IP behind Cloudflare?
Cloudflare adds the CF-Connecting-IP header with the original client address. On nginx I use the realip module: set_real_ip_from for each Cloudflare IP range and real_ip_header CF-Connecting-IP, so $remote_addr is the visitor in logs and limits. It is important to trust the header only from Cloudflare ranges; otherwise anyone can spoof their IP by sending the header themselves. In the application I configure trusted proxies so it reads the right client IP and scheme. Without this, rate limiting and fail2ban see only Cloudflare and may block legitimate traffic or Cloudflare itself.
Q5What should and should not be cached at the edge?
Static, versioned assets such as JavaScript, CSS, fonts and images can be cached for a long time, ideally with hashed filenames so a deploy changes the URL. Public HTML that is the same for every visitor, like marketing pages or blog posts, can be cached briefly with a purge on publish. Anything personalized must bypass the cache: pages for logged-in users, carts, checkout, account pages, admin areas and most API responses. I write Cache Rules that bypass when a session cookie is present and check cf-cache-status to confirm behaviour. Caching a private page once can leak one user's data to the next visitor.
Q6Does Cloudflare protect my server if the origin IP is known?
Only partly. Cloudflare protects traffic that goes through its proxy. If attackers know the origin IP, they can send requests straight to the server and bypass the WAF, rate limits and DDoS protection. The IP leaks through old DNS history, DNS-only records, email headers from the same server, or certificate transparency combined with scanning. So I lock the origin: the firewall accepts ports 80 and 443 only from Cloudflare ranges, or I use Cloudflare Tunnel with no open web ports. If the IP was exposed before, moving to a new IP after locking down is worth considering.
Senior-level questions
S1How would you put a high-traffic Laravel or Next.js site behind Cloudflare safely?
Start with correctness: Full (strict) with an origin certificate, real IP restoration in nginx and trusted proxies in the framework, so sessions, rate limits and HTTPS detection work. Lock the origin with firewall rules plus Authenticated Origin Pulls, or use Tunnel. Then caching by design: hashed static assets cached for a year, public pages cached briefly with stale-while-revalidate and a purge on deploy or publish, and an explicit bypass for any request with a session cookie. Security: managed WAF, rate limits on authentication endpoints, and custom rules for admin paths. The trade-off is that cache rules become part of the application's behaviour, so they live in version control through Terraform or the API, not only in the dashboard.
S2What are the risks of depending on one CDN provider, and how would you reduce them?
A provider outage, an account problem or a bad global configuration push can take every site behind it offline at once, and features like Workers, rules and Tunnel create lock-in. For most small and mid-size projects the benefit outweighs this, but I reduce the blast radius: keep a documented way to switch records to DNS only and serve straight from a hardened origin, keep origin TLS valid independently of the CDN, and export the zone and rules regularly. Infrastructure as code makes the configuration reproducible. Large businesses go further with multi-CDN behind DNS steering or a secondary DNS provider, but that doubles configuration, monitoring and cost, so it needs a clear availability requirement.
S3When is Cache Everything with HTML a good idea?
When pages are identical for all visitors and change on a known event: blogs, documentation, marketing pages and catalogue pages without prices per user. Then caching HTML at the edge removes most origin load and gives low TTFB worldwide. It needs three safeguards: a bypass rule for session cookies, admin, API, cart and checkout paths; a short edge TTL or an event-driven purge so updates appear quickly; and origin headers that mark private responses as private, no-store as a second line of defence. Personalised fragments can load through small API calls. It is a bad idea when content depends on cookies, geography, A/B tests or authorization, unless the cache key explicitly includes those dimensions.
Glossary
- Proxied record
- A DNS record with the orange cloud: visitors connect to Cloudflare, which forwards traffic to your origin.
- Edge certificate
- The certificate Cloudflare shows to visitors; separate from the origin certificate on your server.
- Origin CA certificate
- A free certificate from Cloudflare that only Cloudflare trusts, used for the edge-to-origin connection.
- Cache key
- The set of request parts (host, path, query, sometimes headers) that identifies one cached object.
- Tiered Cache
- Edge locations ask a regional upper tier before the origin, reducing origin load and raising hit ratio.
- WAF
- Web Application Firewall: rules that inspect HTTP requests and block or challenge malicious patterns.
- Managed challenge
- Cloudflare decides automatically whether to show an invisible check or an interactive challenge to a visitor.
- Authenticated Origin Pulls
- Mutual TLS where Cloudflare presents a client certificate so the origin accepts only Cloudflare connections.
- Cloudflare Tunnel
- An outbound-only connection from a cloudflared agent on your server, so no inbound web ports are needed.
- cf-ray
- A unique ID for each request through Cloudflare, useful when reading logs or contacting support.
Keep learning
Have a product in mind?Let's build it.
Tell me about your idea, your product or your team. I usually reply within one working day.