Blocking Direct-to-Origin Access with a Cloud Firewall

Verification

From a machine outside your trusted networks — a mobile hotspot, a VPN exit node you don't control, or a friend's connection — test each path:

# 1. Web traffic through Cloudflare — should return your site
curl -sI https://example.com | head -1
# Expected: HTTP/2 200

# 2. Direct IP on port 443 — should time out
curl -sI --connect-timeout 10 https://203.0.113.50 -H "Host: example.com"
# Expected: curl: (28) Connection timed out after 10000 milliseconds

# 3. Direct IP on port 80 — should time out
curl -sI --connect-timeout 10 http://203.0.113.50 -H "Host: example.com"
# Expected: curl: (28) Connection timed out after 10000 milliseconds

# 4. SSH on custom port — should prompt for authentication
ssh -p 2264 user@example.com
# Expected: password or key prompt

The timeout is the important result. A connection refused (RST packet) would confirm a server exists at that address. A timeout means the firewall dropped the SYN packet silently — the scanner gets nothing back and can't distinguish your IP from an address with nothing behind it.

For a full appliance-level health check, combine this with the one-liner from the Cloudflare Edge Configuration guide — nginx status, PHP-FPM status, certificate expiry, and HSTS presence across all domains.


What this doesn't replace

The cloud firewall is one layer. Each layer below it remains essential:

Cloudflare edge rules
Managed Challenges and custom WAF rules stop bots at the DNS level before traffic reaches the cloud firewall. Covered in the Cloudflare Edge Configuration guide.
Nginx deny rules
If a request does reach nginx — from Cloudflare or from a trusted internal process — the deny all blocks on sensitive paths, hidden files, and known attack patterns are your defense in depth. Covered in Securing Nginx and PHP and the CMS installation guides.
fail2ban + nftables
Host-level intrusion prevention for SSH brute-force and repeat offenders that do pass through Cloudflare. Covered in the fail2ban guide.
Real IP restoration
Without real_ip_header CF-Connecting-IP, every request in your access logs appears to come from Cloudflare. Rate limiting and fail2ban are blind without it. Covered in the Cloudflare Real IP guide.

Provider notes

This guide uses Linode's Cloud Firewall as the reference implementation, but the same architecture works on any provider with a firewall at the infrastructure layer:

  • Vultr Firewall — per-instance firewall groups, similar rule structure, supports CIDR sources
  • DigitalOcean Cloud Firewall — applied per-instance or per-tag, same inbound/outbound rule model
  • AWS Security Groups — stateful by default (return traffic is automatically allowed), but the same allowlist approach applies. Use Security Group rules with Cloudflare's CIDR ranges as sources
  • Hetzner Firewall — stateless, applied per-server, supports up to 10 rules per direction. Cloudflare's 22 ranges fit within the limit if you combine adjacent IPv4 blocks where possible

The principles are identical: restrict ports 80 and 443 to Cloudflare's published IP ranges, keep SSH on a custom port open to the world with host-level fail2ban protection, and set the default inbound policy to DROP. Everything else is provider-specific syntax.

Don't use Cloudflare? The default-inbound-DROP posture is still worth it. Keep open only the ports you know you use. If you run a web server, that's 80 and 443. If you SSH in, that's 22 or a custom port. Everything else — mail ports, database ports, random services that shipped with the distribution image — stays closed. The traditional VPS assumption of "leave everything open and let the kernel sort it out" means your server accepts connections on ports you didn't even realize were listening. A cloud firewall that drops everything except the two or three ports you explicitly allow is a zero-cost, zero-maintenance win regardless of whether Cloudflare is in the picture.

Technical Audit Summary

This guide documents a cloud firewall configuration that drops all direct-to-origin web traffic, allowing only Cloudflare-proxied requests and SSH on a custom port to reach the server. The architecture is portable across any provider with an infrastructure-level firewall.

Last Audit: May 2026
Environment: Debian Trixie (13)
Nginx: 1.31.2
PHP-FPM: 8.5.6

Compatibility: The cloud firewall rules described here apply to Linode Cloud Firewall, Vultr Firewall, DigitalOcean Cloud Firewall, AWS Security Groups, and Hetzner Firewall — any provider that offers stateless or stateful packet filtering at the infrastructure layer. Cloudflare IP ranges are current as of publication; ranges change infrequently but should be verified against cloudflare.com/ips.

2026-05-28: Initial publication — cloud firewall architecture for blocking direct-to-origin access, including loopback nginx configuration, certificate renewal considerations for multi-domain Let's Encrypt certificates with unproxied subdomains, and verification commands. Cross-linked to Cloudflare Edge Configuration, Cloudflare Real IP, fail2ban, Hardening SSH, Let's Encrypt, and Securing Nginx and PHP guides.