Today’s VeloCloud news is one more reminder: admin web interfaces shouldn’t answer to the whole internet. Here’s the simple nginx pattern I use in front of admin pages, including WordPress.
1. Decide who’s allowed
Usually that’s your VPN range and your admin network. The examples below use documentation ranges (192.0.2.0/24 and 198.51.100.0/24). Swap in your own.
2. Allow those, deny everyone else
location /admin/ {
allow 192.0.2.0/24; # admin network
allow 198.51.100.0/24; # VPN
deny all;
proxy_pass http://127.0.0.1:8080;
}
nginx checks allow/deny rules in order and stops at the first match.
3. WordPress version
Lock the login page and wp-admin, but leave admin-ajax.php open because front-end features use it. Nested locations inherit the parent’s allow/deny, so the inner block needs its own allow all;:
location = /wp-login.php {
allow 192.0.2.0/24;
deny all;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php-fpm.sock;
}
location /wp-admin/ {
location = /wp-admin/admin-ajax.php { allow all; try_files $uri =404; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass unix:/run/php/php-fpm.sock; }
allow 192.0.2.0/24;
deny all;
try_files $uri $uri/ /index.php?$args;
}
Match the fastcgi_pass socket to whatever your server already uses.
4. Behind a proxy or CDN?
If traffic comes through a load balancer or CDN, nginx sees the proxy’s address. Set up the real IP module with only your trusted proxy ranges first, or your rules will allow or block the wrong thing.
5. Test and reload
sudo nginx -t && sudo systemctl reload nginx
curl -I https://example.com/wp-login.php # from outside: expect 403
Keep a console or out-of-band way in before you lock yourself out. This isn’t a replacement for patching, but it shrinks who can even try.
Have a blessed day! And Happy Hacking!