Skip to content

Instantly share code, notes, and snippets.

@mike-moreau
Last active March 5, 2026 01:54
Show Gist options
  • Select an option

  • Save mike-moreau/aca8fdacf7fb78d3bf83334a22ffbd7d to your computer and use it in GitHub Desktop.

Select an option

Save mike-moreau/aca8fdacf7fb78d3bf83334a22ffbd7d to your computer and use it in GitHub Desktop.
Allow only cloudflare IP addresses to prevent firewall bypass (Laravel Forge)
# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/www.example.com/before/*;
# Check the raw TCP connection IP ($realip_remote_addr) against known Cloudflare IP ranges.
# Using $realip_remote_addr instead of $remote_addr ensures this check is always against
# the actual connecting IP, even after the realip module restores the visitor's real IP.
# Sets $cloudflare_ip to 1 if the connection is from Cloudflare, 0 otherwise.
geo $realip_remote_addr $cloudflare_ip {
default 0;
# Cloudflare IPv4 ranges (source: https://www.cloudflare.com/ips-v4)
103.21.244.0/22 1;
103.22.200.0/22 1;
103.31.4.0/22 1;
104.16.0.0/13 1;
104.24.0.0/14 1;
108.162.192.0/18 1;
131.0.72.0/22 1;
141.101.64.0/18 1;
162.158.0.0/15 1;
172.64.0.0/13 1;
173.245.48.0/20 1;
188.114.96.0/20 1;
190.93.240.0/20 1;
197.234.240.0/22 1;
198.41.128.0/17 1;
# Cloudflare IPv6 ranges (source: https://www.cloudflare.com/ips-v6)
2400:cb00::/32 1;
2405:8100::/32 1;
2405:b500::/32 1;
2606:4700::/32 1;
2803:f800::/32 1;
2a06:98c0::/29 1;
2c0f:f248::/32 1;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.example.com;
server_tokens off;
root /home/forge/www.example.com/public;
# FORGE SSL (DO NOT REMOVE!)
ssl_certificate /etc/nginx/ssl/www.example.com/1976602/server.crt;
ssl_certificate_key /etc/nginx/ssl/www.example.com/1976602/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ;
ssl_prefer_server_ciphers off;
ssl_dhparam /etc/nginx/dhparams.pem;
# ---------------------------------------------------------------------------
# Cloudflare real IP restoration
# Cloudflare proxies all traffic, so $remote_addr will always be a Cloudflare
# IP. These directives instruct nginx to trust Cloudflare IPs and restore the
# real visitor IP from the CF-Connecting-IP header, making it available as
# $remote_addr throughout the rest of the config (e.g. for logging, rate
# limiting, and PHP via REMOTE_ADDR).
# ---------------------------------------------------------------------------
real_ip_header CF-Connecting-IP;
# IPv4
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
# IPv6
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;
# ---------------------------------------------------------------------------
# Cloudflare-only access enforcement
# Block any request that didn't come through Cloudflare's proxy. This works
# in conjunction with the geo block above, which sets $cloudflare_ip based
# on the raw TCP connection IP ($realip_remote_addr) — not the restored
# visitor IP — so the check is accurate regardless of the realip module.
# ---------------------------------------------------------------------------
if ($cloudflare_ip != 1) {
return 403;
}
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
add_header Surrogate-Control "public, max-age=86400";
add_header Cache-Control "public, max-age=120";
index index.html index.htm index.php;
charset utf-8;
# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/www.example.com/server/*;
#- Enable serving of static gzip files as per https://docs.nginx.com/nginx/admin-guide/web-server/compression/#sending-compressed-files
gzip_static on;
#- Send would-be 404 requests to the cache path or Craft
location / {
try_files $cache_path $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
access_log off;
error_log /var/log/nginx/www.example.com-error.log error;
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
# FORGE CONFIG (DO NOT REMOVE!)
include forge-conf/www.example.com/after/*;
@mike-moreau

Copy link
Copy Markdown
Author

Periodically needs to be updated from https://www.cloudflare.com/en-gb/ips/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment