Last active
March 5, 2026 01:54
-
-
Save mike-moreau/aca8fdacf7fb78d3bf83334a22ffbd7d to your computer and use it in GitHub Desktop.
Allow only cloudflare IP addresses to prevent firewall bypass (Laravel Forge)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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/*; |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Periodically needs to be updated from https://www.cloudflare.com/en-gb/ips/