How to block an IP address on your website

To block an IP address on your website, deny it where requests reach you: cPanel's IP Blocker or a Require not ip line in .htaccess on Apache, a deny line in nginx, or an iptables DROP rule on the server. If the site is behind Cloudflare, block there or restore the real visitor IP first.

Before you block: find the right IP and rule out crawlers

Start with the access log, not a guess. Every request line in a standard Apache or nginx access log begins with the client IP. Count which addresses send the most requests:

awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20

A high count alone does not make an IP bad. Look for the pattern that actually hurts you: hundreds of hits on /wp-login.php, a crawl of every search or filter URL, requests at a steady machine pace all night. Block the source of that pattern, nothing more.

Then check the IP is not a search crawler. User agents are easy to fake, so do not trust a line that says Googlebot or Bingbot, and do not trust one that says something else either. Google documents a two-step check:

  1. Run a reverse DNS lookup on the IP, for example host 66.249.66.1. A real Google crawler resolves to a name ending in googlebot.com, google.com or googleusercontent.com.
  2. Run a forward lookup on that name. It must return the same IP you started with.

Google also publishes its crawler addresses as JSON files, including a separate list for special crawlers such as AdsBot, which checks your ad landing pages. Before you block a whole range, compare it with our list of Googlebot IP ranges, or paste a single address into Verify Googlebot. Apply the same caution to any IP that claims to be Bingbot.

Block an IP in cPanel with IP Blocker

On shared hosting, cPanel's IP Blocker is the easiest way. Open cPanel, go to the Security section and click IP Blocker. If you do not see it, your host has turned the feature off.

The field accepts several formats:

  • A single IP: 203.0.113.45
  • A range: 203.0.113.10-203.0.113.40
  • An implied range: 203.0.113.10-40
  • CIDR: 198.51.100.0/24
  • An implied address: 10., which blocks every IP that begins with 10

Be careful with the implied form. 10. covers millions of addresses. cPanel splits ranges into CIDR blocks and writes them to your site's .htaccess file. It blocks only the website, not other services such as Webmail. To remove a block, click Delete next to it in the list.

Block an IP with .htaccess on Apache

On Apache 2.4, the current syntax lives in mod_authz_core and mod_authz_host. Allow everyone, then exclude the addresses you want out:

<RequireAll>
    Require all granted
    Require not ip 203.0.113.45
    Require not ip 198.51.100.0/24
    Require not ip 2001:db8:3::/48
</RequireAll>

The RequireAll wrapper matters. A negated Require not can only fail or stay neutral, so it can never grant access on its own. It needs Require all granted next to it inside the same block.

Require ip takes a full address, a partial one (Require not ip 10.1 covers 10.1.0.0 to 10.1.255.255), a network with a netmask, CIDR, and IPv6. For these lines to work in .htaccess, the server must allow AuthConfig overrides. Most hosts do.

Many tutorials still show Order Deny,Allow and Deny from. That is Apache 2.2 syntax, kept alive by mod_access_compat. Apache's documentation calls it deprecated and asks you to avoid tutorials that recommend it.

WordPress on Apache uses the same file. It sits in the WordPress root, and WordPress edits it for pretty permalinks, so put your block above the # BEGIN WordPress section, not inside it. No plugin is needed. If your host runs nginx only, .htaccess has no effect and you need the next method.

Block an IP in nginx

nginx blocks addresses with deny and allow from ngx_http_access_module. They work in the http, server and location blocks and accept a single address, CIDR or all.

Order is the one rule to remember: nginx checks the lines in sequence and stops at the first match. A deny placed after allow all never fires.

For anything longer than a few lines, keep the list in its own file and include it:

# /etc/nginx/blocklist.conf
deny 203.0.113.45;
deny 198.51.100.0/24;
deny 2001:db8:3::/48;
server {
    server_name example.com;
    include /etc/nginx/blocklist.conf;
    # ... the rest of your site config
}

Apply it with nginx -s reload. nginx checks the syntax first and, if the new file is broken, keeps running on the old configuration.

If the same scraper keeps coming back from fresh addresses, a growing deny list is a losing game. Limiting how fast any one client can request pages often works better: see rate limiting in nginx.

Block an IP with iptables and ipset

iptables drops packets in the Linux kernel before they reach your web server. It needs root, so it is for a VPS or dedicated server, not shared hosting.

iptables -I INPUT -s 203.0.113.45 -j DROP
iptables -I INPUT -s 198.51.100.0/24 -j DROP
iptables -I INPUT -m iprange --src-range 192.0.2.10-192.0.2.50 -j DROP
ip6tables -I INPUT -s 2001:db8:3::/48 -j DROP

# list rules with their numbers, then delete one
iptables -L INPUT -n --line-numbers
iptables -D INPUT 1

-I puts the rule at the top of the chain, so it wins over any ACCEPT rule further down. -A would add it at the end, where an earlier rule may already let the traffic in. IPv6 needs ip6tables; an IPv4 rule does not cover it.

For long lists, put the addresses in an ipset and match the whole set with one rule:

ipset create blocklist hash:net
ipset add blocklist 198.51.100.0/24
ipset add blocklist 203.0.113.45
iptables -I INPUT -m set --match-set blocklist src -j DROP

A hash set holds up to 65,536 entries by default. Two cautions. First, an iptables block shuts the IP out of the whole server, including SSH and mail, so never block your own office or your monitoring service. Second, rules added this way usually live only in memory. Save them with your distribution's method (iptables-save and ipset save, or the firewall tool your server uses, such as ufw) or they disappear at the next reboot.

The Cloudflare trap: your server sees the wrong IP

This is where most IP blocks silently fail. When a site is proxied through Cloudflare or another CDN, visitors connect to the proxy, and the proxy connects to your server. Without extra setup, your server logs Cloudflare's addresses, not your visitors'.

That has three consequences:

  • An iptables rule for a visitor IP does nothing, because that IP never connects to your server.
  • .htaccess, cPanel and nginx rules see Cloudflare's IP too, unless the real IP is restored.
  • Blocking an IP you found in such a log blocks a Cloudflare address, which carries real customers and search crawlers along with the bot.

Cloudflare passes the visitor's address in the CF-Connecting-IP header. You have two fixes.

Restore the real IP at the server. On Apache, mod_remoteip goes in the server or virtual host config (not .htaccess), with one trusted proxy line per Cloudflare range from cloudflare.com/ips:

RemoteIPHeader CF-Connecting-IP
RemoteIPTrustedProxy 173.245.48.0/20
RemoteIPTrustedProxy 103.21.244.0/22
# ...one line per Cloudflare range

After that, Require not ip matches the real visitor. Also change %h to %a in your LogFormat so the log shows it. On nginx, the realip module does the same job. It is not built by default, so check that your build has it:

set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
# ...one line per Cloudflare range
real_ip_header CF-Connecting-IP;

Cloudflare's range list changes over time, so update these lines when it does. Never trust the header from addresses outside the list, or anyone can claim any IP.

Or block at the edge. A Cloudflare rule stops the request before it costs your server anything, and Cloudflare already knows which crawlers are verified. See how to block an IP in Cloudflare.

Which method to use

MethodLayerNeeds rootWorks behind CloudflareBest for
cPanel IP BlockerWeb server (.htaccess)NoOnly if the host restores the visitor IPShared hosting, a few IPs
.htaccess Require not ipApacheNoOnly with mod_remoteipApache and WordPress sites
nginx denynginxUsuallyOnly with the realip moduleVPS, longer lists via include
iptables / ipsetKernel firewallYesNoServers without a proxy, blocking all ports
Cloudflare ruleEdgeNoYesAny site already on Cloudflare

Whatever you choose, check your crawl stats in Search Console a few days later. In the traffic reports we have prepared, the blocks that hurt SEO were rarely single IPs. They were range, network or user agent rules that caught Google's or Bing's crawlers along the way. If you are not sure who is loading your server, a free bot traffic report shows the networks and patterns before you block anything.

FAQ

How do I block an IP range?

Use CIDR notation, which every method here accepts: 198.51.100.0/24 covers 256 addresses. cPanel also takes a dash range, and iptables has --src-range for ranges that do not fit a CIDR block. Check the range against Google's published crawler lists first.

Why can I still see a blocked IP in my logs?

Most often the site is behind Cloudflare or another proxy, so the block matches the proxy's address, not the visitor's. Other causes: an nginx allow line placed before your deny, an iptables rule appended below an ACCEPT rule, or the visitor arriving over IPv6.

How do I block an IP in WordPress without a plugin?

On Apache hosting, add a RequireAll block with Require not ip to the .htaccess file in your WordPress root, above the WordPress section. On nginx hosting, ask the host to add a deny line or use your CDN.

Can blocking IP addresses hurt my SEO?

Only if you block a search or ad crawler. Blocking a scraper does no harm. Verify any IP that might be Googlebot or Bingbot with a reverse and forward DNS lookup before blocking it, and be extra careful with whole ranges.

Rather not deal with this yourself?

If bots slow down your site, copy it or fill your analytics with fake visits, we can sort it out. We find out which bots visit your site, stop the harmful ones and check that Google, Bing and ad crawlers still get in. It starts with a free report.

Get a free bot report