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 .htaccess or 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 granted allows everyone, and then the addresses listed in Require not ip are excluded.
  • Require not ip cannot be used on its own; it has to be inside RequireAll like this.
  • Writing 198.51.100.0/24 covers the whole range from 198.51.100.0 to 198.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 (RemoteIPHeader and RemoteIPTrustedProxy), or Nginx with real_ip_header and set_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 /16 can 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 RequireAll and Require not ip at the top of .htaccess. Order / Deny is the old syntax.
  • On Nginx, add deny to the server block, check with nginx -t, then reload.
  • With ufw, use insert 1 to 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)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲