2026年11月13日
2026年11月13日
How to Block an IP Address in WordPress: .htaccess, Nginx, Firewall
This article is a localized edition of the Japanese original. Read the Japanese original
Brute-force attacks on the login screen and spam comments sometimes keep coming from the same IP address. In that case, refusing that IP address at the web server stops the requests before WordPress (PHP) even runs, which also takes load off the server.
This article shows how to block specific IP addresses with Apache's .htaccess, with Nginx, and with the Linux firewall (ufw), in that order. If you want the opposite, letting only your own IP address into the admin area, see "How to protect the WordPress wp-admin directory".
Before you start
- Copy the original file before you edit
.htaccessor an Nginx configuration file. A typo can turn the whole site into a 500 error. - So you don't block yourself, check your current IP address first, for example by searching "what is my IP" in your browser.
- The IP addresses in this article (
203.0.113.10,198.51.100.0/24,2001:db8::/32) are examples. Replace them with the addresses you actually want to block.
Step 1: Find the IP address to block
First, look in the access log for the IP address that sends the problem requests. The log location depends on your setup.
| Environment | Access log location (default) |
|---|---|
| Apache on Ubuntu / Debian | /var/log/apache2/access.log |
| Apache on RHEL / AlmaLinux / Rocky Linux | /var/log/httpd/access_log |
| Nginx | /var/log/nginx/access.log |
On shared hosting, you can usually download access logs from the control panel, so check your host's documentation.
This example counts POST requests to wp-login.php per IP address (Apache on Ubuntu).
sudo grep "POST /wp-login.php" /var/log/apache2/access.log \
| awk '{print $1}' | sort | uniq -c | sort -rn | head -20
The number on the left is the count and the value on the right is the IP address. Addresses with high counts are candidates for blocking.
Step 2: Block with Apache .htaccess
Current Apache (2.4) uses the Require directive for access control. Open the .htaccess file in the WordPress root (the directory that contains wp-config.php) and add the following at the top (above the # BEGIN WordPress line).
<RequireAll>
Require all granted
Require not ip 203.0.113.10
Require not ip 198.51.100.0/24
Require not ip 2001:db8::/32
</RequireAll>
Require all grantedallows everyone, and then the addresses listed inRequire not ipare excluded.Require not ipcannot be used on its own; it has to be insideRequireAlllike this.- Writing
198.51.100.0/24covers the whole range from198.51.100.0to198.51.100.255. IPv6 works the same way.
WordPress rewrites everything between # BEGIN WordPress and # END WordPress, for example when you save the permalink settings, so do not put your rules in that section.
About the old syntax (Order / Deny)
Order Deny,Allow and Deny from are Apache 2.2 syntax. In Apache 2.4 they only work when the mod_access_compat module is loaded. The official Apache documentation advises against mixing the old directives with Require. For new rules, use the Require form shown above.
If .htaccess has no effect
Require in .htaccess only works when the server configuration allows AuthConfig (or All) in AllowOverride. Otherwise you get a 500 error or the rules are ignored. If you run Apache yourself, for example on a VPS, you can put the same rules in the virtual host configuration (inside <Directory>) instead of .htaccess. After changing configuration files, check the syntax and then reload.
sudo apachectl configtest
sudo systemctl reload apache2 # use httpd on RHEL-based systems
Step 3: Block with Nginx
Nginx has no .htaccess, so you add deny lines to the site's configuration file (the server block).
server {
# ...existing settings...
deny 203.0.113.10;
deny 198.51.100.0/24;
deny 2001:db8::/32;
}
In the server block the rules apply to the whole site. Inside location = /wp-login.php { ... } they apply only to the login screen, but then that location also needs the PHP handling (such as fastcgi_pass).
After changing the configuration, check the syntax and then reload.
sudo nginx -t
sudo systemctl reload nginx
Step 4: Block with the firewall (ufw)
If you have many addresses to block, or you also want to stop non-web traffic, you can refuse them in the OS firewall. Here is an example with ufw on Ubuntu.
sudo ufw insert 1 deny from 203.0.113.10 to any
sudo ufw status numbered
ufw checks rules from the top and uses the first one that matches. If you append ufw deny from … to the end, the request may match an earlier rule that allows ports 80 and 443 and never get blocked, so insert 1 puts the rule first. To remove it, look up its number with sudo ufw status numbered and delete it with sudo ufw delete NUMBER.
Step 5: Check that the block works
With Apache and Nginx, requests from a blocked IP address get 403 Forbidden. Check the access log and confirm that requests from that address now have status 403.
sudo tail -f /var/log/apache2/access.log | grep "203.0.113.10"
Also confirm in your own browser that the site and the login screen still load normally. If you blocked the address with ufw, the connection itself is refused, so nothing shows up in the access log.
If you use a CDN or proxy
When traffic goes through a CDN such as Cloudflare or through a load balancer, the web server sees the IP address of the CDN or proxy as the client. Putting a visitor's IP address in Require not ip or deny then blocks nothing. And if you block a CDN IP address, the whole site becomes unreachable.
In that case, use one of these approaches:
- Block at the CDN: On Cloudflare, for example, use IP Access rules or another feature in the CDN dashboard.
- Tell the web server the real client address: Configure Apache with
mod_remoteip(RemoteIPHeaderandRemoteIPTrustedProxy), or Nginx withreal_ip_headerandset_real_ip_from, so that the IP address in the header sent by a trusted proxy is treated as the client. If you don't restrict the trusted proxy range correctly, the header can be spoofed.
Blocking with a plugin
Security plugins such as Wordfence also have a feature that blocks an IP address or a range of addresses. The advantage is that you can manage it from the admin screen, but the check runs after WordPress (PHP) has loaded, so it does less to reduce server load than blocking at the web server or firewall. Check each plugin's official documentation for the exact steps.
Things to keep in mind
- IP addresses change: Attackers often switch addresses and keep going, so IP blocking alone is not enough. Combine it with strong passwords, two-factor authentication, IP restrictions on the login screen and so on.
- Don't block wide ranges: A wide range such as
/16can lock out ordinary users of the same provider. - Keep the list tidy: If you leave blocks in place forever, you may refuse legitimate visitors once an address is reassigned to someone else. Note the date and reason in a comment (a line starting with
#) and review the list regularly. - If your login screen gets a lot of attacks, also consider "How to restrict the WordPress login page by IP address". If you end up with a 403 yourself, see "Fixing 403 errors caused by IP restrictions".
Summary
- Find the IP address sending the problem requests in the access log.
- On Apache 2.4, add
RequireAllandRequire not ipat the top of.htaccess.Order/Denyis the old syntax. - On Nginx, add
denyto theserverblock, check withnginx -t, then reload. - With ufw, use
insert 1to put the rule first. - Behind a CDN or proxy, block at the CDN or configure the web server to see the real client address.
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)