Back to Learn

Block Access Using .htaccess by IP Address | NOC.org

Why Block IPs with .htaccess?

Blocking access by IP address is one of the most basic yet effective security measures for web servers. Common scenarios include blocking attackers conducting brute force attacks, restricting access to staging or admin areas, blocking scrapers and abusive bots, and responding to ongoing attacks before a WAF rule can be deployed.

Apache's .htaccess file provides a convenient way to implement IP-based access control without modifying the main server configuration or restarting Apache. Changes take effect immediately.

Apache 2.4 Require Syntax

Apache 2.4 replaced the older Allow/Deny/Order directives with the Require directive. If you are running Apache 2.4 or later (which includes all current Ubuntu, Debian, and CentOS distributions), use this syntax:

Block a Single IP Address

<RequireAll>
    Require all granted
    Require not ip 203.0.113.50
</RequireAll>

This allows all traffic except from 203.0.113.50, which receives a 403 Forbidden response.

Block Multiple IP Addresses

<RequireAll>
    Require all granted
    Require not ip 203.0.113.50
    Require not ip 198.51.100.23
    Require not ip 192.0.2.100
</RequireAll>

Block a CIDR Range

To block an entire subnet, use CIDR notation:

<RequireAll>
    Require all granted
    Require not ip 203.0.113.0/24
    Require not ip 198.51.100.0/24
</RequireAll>

This blocks all 256 addresses in each /24 subnet.

Allow Only Specific IPs (Whitelist)

To restrict access to only specific IP addresses (useful for admin panels or staging sites):

<RequireAny>
    Require ip 10.0.0.0/8
    Require ip 172.16.0.0/12
    Require ip 192.168.1.100
</RequireAny>

Everyone else receives a 403 Forbidden response. This is commonly used to protect /wp-admin/, /phpmyadmin/, or other sensitive paths.

Legacy Apache 2.2 Syntax

If you are still running Apache 2.2 (end of life, but still found on older servers), the syntax uses Order, Allow, and Deny:

# Block specific IPs (Apache 2.2)
Order Allow,Deny
Allow from all
Deny from 203.0.113.50
Deny from 198.51.100.0/24

# Allow only specific IPs (Apache 2.2)
Order Deny,Allow
Deny from all
Allow from 192.168.1.100
Allow from 10.0.0.0/8

Upgrade to Apache 2.4 when possible. The 2.2 syntax is deprecated and no longer receives security updates.

Combining with mod_rewrite for Custom Error Pages

The standard 403 Forbidden page is generic. You can use mod_rewrite to serve a custom page or redirect blocked visitors:

RewriteEngine On

# Block specific IPs and redirect to a custom page
RewriteCond %{REMOTE_ADDR} ^203\.0\.113\.50$
RewriteRule ^ /blocked.html [L,R=403]

# Block a range
RewriteCond %{REMOTE_ADDR} ^198\.51\.100\.
RewriteRule ^ - [F]

The [F] flag returns a 403 Forbidden. The first example redirects to a custom blocked.html page with a 403 status code.

Block by User-Agent (Bonus)

You can also combine IP blocking with user-agent blocking to target specific bots:

RewriteEngine On

# Block known bad bots by user-agent
RewriteCond %{HTTP_USER_AGENT} (BadBot|EvilScraper|MaliciousCrawler) [NC]
RewriteRule ^ - [F]

# Block specific IP AND user-agent combination
RewriteCond %{REMOTE_ADDR} ^203\.0\.113\.50$
RewriteCond %{HTTP_USER_AGENT} python-requests [NC]
RewriteRule ^ - [F]

Blocking by Country with GeoIP

If you need to block traffic from entire countries, you can use Apache's GeoIP module with .htaccess. First, install the module:

sudo apt install libapache2-mod-geoip
sudo a2enmod geoip
sudo systemctl restart apache2

Then in .htaccess:

# Set the GeoIP database
GeoIPEnable On

# Block traffic from specific countries
RewriteEngine On
RewriteCond %{ENV:GEOIP_COUNTRY_CODE} ^(CN|RU|KP)$
RewriteRule ^ - [F]

Country-level blocking is a blunt instrument and should be used carefully. Legitimate users behind VPNs or traveling may be affected.

Protecting Specific Directories

You can place .htaccess files in specific directories to restrict access to only those paths:

# /var/www/html/admin/.htaccess
# Only allow office IP and VPN
<RequireAny>
    Require ip 203.0.113.10
    Require ip 10.8.0.0/24
</RequireAny>
# /var/www/html/api/.htaccess
# Block known abusive IPs from the API
<RequireAll>
    Require all granted
    Require not ip 198.51.100.0/24
</RequireAll>

.htaccess vs iptables vs WAF

Each method of blocking IPs operates at a different layer:

Method Layer Pros Cons
.htaccess Application (Apache) No root required, immediate effect, per-directory control Only works with Apache, performance overhead on every request, traffic still reaches the server
iptables/nftables Network (kernel) Blocks traffic before it reaches the web server, very fast, low overhead Requires root, applies server-wide, harder to manage per-site
WAF Edge (CDN/proxy) Blocks traffic before it reaches your server, advanced rules (rate limiting, bot detection), centralized management Requires external service, may add latency, cost

For best security, use a layered approach: a WAF at the edge to filter the majority of malicious traffic, iptables on the server for network-level protection, and .htaccess for application-specific access control.

Testing with curl

After adding IP blocks, verify they work correctly:

# Test from the blocked IP (or using a proxy)
curl -I https://example.com
# Expected: HTTP/1.1 403 Forbidden

# Test from an allowed IP
curl -I https://example.com
# Expected: HTTP/1.1 200 OK

# Test a specific path
curl -I https://example.com/admin/
# Expected: 403 if your IP is not whitelisted

# Simulate a different IP using X-Forwarded-For (only works if Apache trusts this header)
curl -H "X-Forwarded-For: 203.0.113.50" -I https://example.com

Note: Be careful with X-Forwarded-For headers. If Apache is configured to use mod_remoteip and trusts the X-Forwarded-For header, attackers can spoof their IP. Only trust this header from known proxies and load balancers.

Managing Large Block Lists

If you need to block hundreds of IPs, managing them in .htaccess becomes unwieldy. Consider these alternatives:

  • Include file: Move the block list to a separate file and include it: Include /etc/apache2/blocklist.conf (requires main config access).
  • iptables/ipset: Use ipset with iptables for managing large IP sets efficiently at the kernel level.
  • fail2ban: Automatically block IPs based on log analysis (failed logins, 404 floods, etc.).
  • WAF: Use a cloud-based WAF that maintains threat intelligence feeds and automatically blocks known malicious IPs.

Summary

Apache's .htaccess file provides a quick and accessible way to block or restrict access by IP address. Use the Apache 2.4 Require syntax for clean, maintainable rules, combine with mod_rewrite for custom responses, and consider country-level blocking with GeoIP when needed. For production environments facing significant attack traffic, pair .htaccess rules with iptables at the network layer and a WAF at the edge for comprehensive protection against brute force attacks and other threats.

Improve Your Websites Speed and Security

14 days free trial. No credit card required.