Field notes
Hardening the origin behind Cloudflare
Putting Cloudflare in front of a site is the easy half. The other half is making the origin behave like it knows it sits behind a proxy: logging the right client, refusing traffic that skipped the edge, and sending headers the cache can trust. These are the settings I check first, using nginx as the example.
1. Restore the real client IP
Behind Cloudflare, every request arrives from a Cloudflare address. Logs, rate limits and IP-based rules on the origin are wrong until the original client IP is restored from the CF-Connecting-IP header, and that header must only be trusted when the request really came from Cloudflare.
# /etc/nginx/conf.d/cloudflare-realip.conf
# Generated from https://www.cloudflare.com/ips-v4 and /ips-v6
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
# ... remaining IPv4 and IPv6 ranges ...
set_real_ip_from 2400:cb00::/32;
real_ip_header CF-Connecting-IP;
Generate this file from Cloudflare's published lists on a schedule instead of pasting it once. If a load balancer sits between Cloudflare and nginx, trust that hop as well and use X-Forwarded-For with real_ip_recursive on.
2. Only accept traffic from the edge
If the origin answers anyone who finds its IP, the WAF, bot rules and rate limits are optional for attackers. In order of strength:
- Firewall allow-list. Only Cloudflare's ranges may reach ports 80 and 443. Simple, but any Cloudflare customer could still route traffic to you through their own zone.
- Authenticated Origin Pulls (mTLS). nginx requires Cloudflare's client certificate. The zone-level certificate proves the request came through Cloudflare; a per-hostname certificate you upload proves it came through your configuration.
- Cloudflare Tunnel.
cloudflaredconnects outbound, and the origin has no inbound ports open at all.
ssl_certificate /etc/nginx/certs/origin.pem; # Cloudflare Origin CA
ssl_certificate_key /etc/nginx/certs/origin.key;
ssl_client_certificate /etc/nginx/certs/origin-pull-ca.pem;
ssl_verify_client on;
Pair this with SSL/TLS mode Full (strict), so the edge validates the origin as well. Then check that no DNS-only record still points straight at the same server.
3. Keepalives and timeouts
Cloudflare reuses connections to the origin. If the origin's idle timeout is shorter than Cloudflare's, the origin can close a connection just as the edge sends a request on it. That race shows up as sporadic 520 errors that are maddening to reproduce. Set keepalive_timeout comfortably above Cloudflare's documented idle timeout for origin connections.
In the other direction, Cloudflare waits a limited time for the origin's response (100 seconds by default) and then returns a 524. Raising nginx's proxy_read_timeout does not change that. Long-running work such as exports or reports belongs in a background job with a status endpoint, not a request that holds the connection open.
4. Send headers the cache can trust
The origin's Cache-Control is the contract with the edge and the browser. Most "Cloudflare is not caching" tickets I see trace back to the origin:
| Content | Header |
|---|---|
Fingerprinted assets (app.3f9c1.js) | public, max-age=31536000, immutable |
| HTML and other content that changes in place | no-cache or a short max-age |
| Personalised or authenticated responses | private, no-store |
- Strip
Set-Cookiefrom static responses. By default, Cloudflare will not cache a response that sets a cookie. - Exclude tracking parameters such as
utm_*from the cache key with a cache rule, so one asset is not cached a hundred times. - Be careful with edge cache rules that override origin headers. A rule that forces a one-year browser TTL on HTML means visitors keep stale pages until they clear their cache.
5. Security headers in one place
HSTS, X-Content-Type-Options, Referrer-Policy and a content security policy can be set at the origin or with Cloudflare response header Transform Rules. Either works. Pick one place, so two layers do not send conflicting values.
Checklist
- Real client IP restored, trusted only from Cloudflare ranges, and refreshed automatically.
- Origin reachable only through Cloudflare: allow-list, mTLS or Tunnel.
- Full (strict) TLS with a valid origin certificate.
- Origin keepalive longer than the edge's; long work moved out of the request path.
- Cache-Control set deliberately per content type, with no stray cookies on assets.