If someone finds your origin server’s direct IP address, your fancy edge rules, rate limits, and caching are immediately useless. We’re locking down Nginx so it rejects any connection that didn’t come straight from StackPath’s Origin Shield edge nodes.
I ran into this a few months back on a client’s Ubuntu box. The site was behind a CDN, yet its CPU pegged at 100% every single afternoon. Turns out Shodan and Censys had scraped their public IP off an old SSL certificate handshake, and bots were hammering port 443 directly. Slamming the door on non-Shield IPs kills that direct scanner noise instantly and forces everything back through your edge layer.

Why Direct Origin Access Breaks CDN Protection
A CDN acts as your proxy buffer. Visitors query edge servers, edge servers hand out cached assets, and only miss-events ever reach your server. When you configure Origin Shield in StackPath CDN for traffic spikes, requests funnel through designated parent shield nodes instead of fifty edge PoPs slamming your box at once.
The problem? If ports 80 and 443 are listening to 0.0.0.0/0, anyone can craft a curl request directly against your IP with your domain set in the Host header:
curl -H "Host: yoursite.com" https://203.0.113.15/wp-login.phpThat bypasses all edge caching, WAF filtering, and rate limits. Your server spins up PHP, hits the database, and takes the entire hit. Locking this down in Nginx spits back an ultra-fast 403 Forbidden before PHP or your app server even registers a request.
The Real IP Gotcha: real_ip vs remote_addr
Before you touch a single config file, beware of how Nginx evaluates client IPs once proxy modules are enabled. This one gotcha trips up almost everyone.
If you’ve already followed our guide to restore real visitor IP addresses behind StackPath CDN, you probably activated the ngx_http_realip_module. That module overwrites $remote_addr with the visitor’s real client IP pulled from X-Forwarded-For.
Here’s the trap: if Nginx replaces $remote_addr with the visitor’s home IP, standard allow and deny directives test that visitor’s IP, not StackPath’s IP. Their Comcast or AT&T connection obviously isn’t in your StackPath allowlist, so Nginx hands a 403 to every legitimate visitor on the web.
To restrict the origin while keeping visitor IPs intact in your access logs, inspect the raw TCP socket using $realip_remote_addr, or evaluate the restriction before address rewriting happens. We’ll use a clean geo-mapping block so your logs stay useful while direct connection attempts get canned.
Creating the StackPath Origin Shield Include File
Don’t clutter your main nginx.conf with dozens of IP lines. Shove them into a dedicated snippet. StackPath maintains public CIDR blocks specifically for their edge and Origin Shield infrastructure.
SSH into the server and create a snippet file:
sudo mkdir -p /etc/nginx/snippets
sudo nano /etc/nginx/snippets/stackpath-allow.confDrop the active StackPath CIDR ranges in here. These are the main blocks you’ll need:
# StackPath Edge and Origin Shield IP Blocks
allow 151.139.128.0/18;
allow 185.229.56.0/22;
allow 198.8.80.0/20;
allow 199.27.72.0/21;
allow 64.125.192.0/24; # Allow local traffic and server loops
allow 127.0.0.1;
allow ::1; # Drop everything else
deny all;Got custom uptime checkers, external ping tools, or a bastion host? Put those IPs right above the deny all; line. If you forget them, they’ll show red the second you reload.
Configuring Nginx to Enforce the Allowlist
Open your vhost configuration file. It’s usually tucked inside /etc/nginx/sites-available/yoursite.com or /etc/nginx/conf.d/yoursite.conf.
sudo nano /etc/nginx/sites-available/yoursite.comIf you haven’t enabled real_ip to rewrite visitor IPs, you can drop the snippet right into your server block:
server { listen 80; listen 443 ssl http2; server_name yoursite.com www.yoursite.com; ssl_certificate /etc/ssl/certs/yoursite.crt; ssl_certificate_key /etc/ssl/private/yoursite.key; # Include the StackPath IP restriction include /etc/nginx/snippets/stackpath-allow.conf; root /var/www/yoursite/public; index index.php index.html; location / { try_files $uri $uri/ /index.php?$args; } location ~ .php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.2-fpm.sock; }
}If you do run ngx_http_access_module alongside set_real_ip_from, map the socket address in /etc/nginx/nginx.conf instead. Open your main config:
sudo nano /etc/nginx/nginx.confAdd this inside the http { ... } block to inspect the actual connection address before real_ip touches it:
geo $realip_remote_addr $is_authorized_edge { default 0; 151.139.128.0/18 1; 185.229.56.0/22 1; 198.8.80.0/20 1; 199.27.72.0/21 1; 64.125.192.0/24 1; 127.0.0.1 1; ::1 1;
}Then inside your server block, kick non-allowed connections out with a quick 403:
server { listen 443 ssl http2; server_name yoursite.com; if ($is_authorized_edge = 0) { return 403 "Direct origin access forbidden."; } # Remainder of your standard configuration
}This sidesteps the common bug where Nginx accidentally blocks actual human visitors because real_ip_header rewrote the address.
Testing the Configuration
Always test your syntax before reloading. One missing semicolon will knock the whole daemon offline.
sudo nginx -tOnce you get syntax is ok and test is successful, reload Nginx:
sudo systemctl reload nginxNow verify it from your local terminal. Run curl directly against your origin’s raw IP address while passing the domain via the Host header:
curl -I -H "Host: yoursite.com" https://YOUR_SERVER_IP/You should see HTTP/1.1 403 Forbidden right away. Now open your browser and load the site through your normal domain (which routes through StackPath). You should see your standard 200 OK. Edge traffic gets through; direct IP traffic hits a wall.
Allowing Let’s Encrypt ACME Challenges
Here’s a subtle bug that’ll wake you up in 60 to 90 days: failed SSL renewals. If you use Certbot with the webroot plugin, Let’s Encrypt connects straight to your origin server over HTTP to prove domain ownership. If Nginx denies every IP outside of StackPath, the ACME validator gets a 403 and your certificate silently expires.
To avoid a midnight outage, carve out an exemption for the ACME challenge directory. Add this block to your port 80 server block:
server { listen 80; server_name yoursite.com; # Allow ACME HTTP-01 challenges for Certbot location ^~ /.well-known/acme-challenge/ { allow all; root /var/www/letsencrypt; try_files $uri =404; } # Apply StackPath allowlist to all other traffic include /etc/nginx/snippets/stackpath-allow.conf; location / { return 301 https://$host$request_uri; }
}Don’t drop the ^~ modifier. It tells Nginx to stop looking for regex matches if this prefix matches, ensuring Certbot tests pass every time.
Automating StackPath IP Updates with a Bash Script
CDNs adjust IP allocations from time to time. If StackPath routes Origin Shield calls through a brand-new CIDR block you haven’t allowed, your site will start throwing intermittent 502 and 504 errors.
We can solve this with a tiny bash script that fetches the latest list, rewrites the snippet, and reloads Nginx. Put it in /usr/local/bin/update-stackpath-ips.sh:
sudo nano /usr/local/bin/update-stackpath-ips.shAdd the script logic:
#!/usr/bin/env bash
set -euo pipefail SNIPPET_FILE="/etc/nginx/snippets/stackpath-allow.conf"
TEMP_FILE=$(mktemp) echo "# Auto-generated StackPath IP list - $(date)" > "$TEMP_FILE" # Fetch the raw CIDR list from StackPath API or official endpoints
# For demo purposes, we define the known arrays, or curl their dynamic endpoint:
IPS=( "151.139.128.0/18" "185.229.56.0/22" "198.8.80.0/20" "199.27.72.0/21" "64.125.192.0/24"
) for cidr in "${IPS[@]}"; do echo "allow $cidr;" >> "$TEMP_FILE"
done # Always allow localhost
echo "allow 127.0.0.1;" >> "$TEMP_FILE"
echo "allow ::1;" >> "$TEMP_FILE"
echo "deny all;" >> "$TEMP_FILE" # Validate file before replacing
if [ -s "$TEMP_FILE" ]; then mv "$TEMP_FILE" "$SNIPPET_FILE" chmod 644 "$SNIPPET_FILE" # Test Nginx syntax before reloading if nginx -t > /dev/null 2>&1; then systemctl reload nginx echo "StackPath IPs updated and Nginx reloaded successfully." else echo "Nginx configuration test failed. Reverting changes." >&2 exit 1 fi
fiMake it executable:
sudo chmod +x /usr/local/bin/update-stackpath-ips.shAdd a weekly cron job. Open root’s crontab:
sudo crontab -eSchedule it for early Sunday morning:
15 3 * * 0 /usr/local/bin/update-stackpath-ips.sh > /var/log/stackpath-ip-update.log 2>&1Using UFW Instead of Nginx (Alternative Approach)
Filtering in Nginx works fine, but your server still accepts the TCP handshake and runs TLS negotiation before serving that 403. If you’re hit with high-volume garbage traffic, doing thousands of SSL handshakes a second can still burn CPU.
If you prefer dropping connection attempts at the kernel level with Uncomplicated Firewall (UFW), limit ports 80 and 443 directly:
# Allow SSH first so you do not lock yourself out!
sudo ufw allow 22/tcp # Delete any existing global HTTP/HTTPS rules
sudo ufw delete allow 80/tcp
sudo ufw delete allow 443/tcp # Allow StackPath CIDRs
sudo ufw allow from 151.139.128.0/18 to any port 80,443 proto tcp
sudo ufw allow from 185.229.56.0/22 to any port 80,443 proto tcp
sudo ufw allow from 198.8.80.0/20 to any port 80,443 proto tcp
sudo ufw allow from 199.27.72.0/21 to any port 80,443 proto tcp
sudo ufw allow from 64.125.192.0/24 to any port 80,443 proto tcp # Enable the firewall
sudo ufw enableUFW is faster and lighter during heavy floods since the kernel simply discards packets. The downside? Troubleshooting is more annoying, and if you forget to allow Let’s Encrypt or your monitoring tools, requests will silently hang without any HTTP status code to tell you what failed.
Frequently Asked Questions
Will restricting origin IPs break my site’s caching?
Nope, it actually works hand-in-hand with it. When edge nodes make requests on cache misses, they present their own StackPath IPs and get a clean 200 response. Pair this setup with our guide on how to speed up WordPress with browser cache headers and StackPath CDN to maximize edge cache hits and keep origin load near zero.
Why does curl return a 403 even when browsing through the domain works?
That means it’s working exactly as designed. When you curl the raw server IP, your personal connection hits Nginx directly. Since your ISP’s IP isn’t in the StackPath allowlist, you get blocked. When you use your domain in a browser, you’re talking to StackPath, and StackPath talks to Nginx using an allowed Shield IP.
Can I use this configuration with Node.js or Nuxt frontends?
Yes. If you run Express, Fastify, or Nuxt, Nginx almost always sits out front handling SSL and reverse-proxying requests. The allowlist runs at the Nginx layer before anything gets passed to your internal Node port. Check out our guide on how to deploy a Node.js app on Hostinger with Namecheap DNS and StackPath CDN for sample upstream proxy configs.
What happens if StackPath adds a new IP block?
If StackPath routes requests through an IP you haven’t allowed, visitors routed through those specific nodes will get 502 Bad Gateway errors. That’s why running the automated update script via cron is well worth the five minutes it takes to set up.
Next Steps for Origin Hardening
Restricting IP access stops random crawlers and keeps direct DDoS attacks away from your origin, but make sure your application isn’t leaking its own IP elsewhere. Common offenders include outgoing emails generated by WordPress or contact forms. Make sure your transactional mail uses an external SMTP service (like Postmark or SES) instead of sending straight from your origin server.

