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:

  1. 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.
  2. 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.
  3. Cloudflare Tunnel. cloudflared connects 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:

ContentHeader
Fingerprinted assets (app.3f9c1.js)public, max-age=31536000, immutable
HTML and other content that changes in placeno-cache or a short max-age
Personalised or authenticated responsesprivate, no-store

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

Seeing 52x errors or poor cache ratios?

I help teams tune edge and origin together through Saber Security.

Get in touch