Apache: Restrict access by IP address

Crude way to stop an attack in case of an emergency.

Keep in mind that this is a static configuration, which is not very effective against adversarial attacks when an attacker constantly changes their tactics.

This guide does not cover managing .htaccess file.

Locating config files

Before applying the changes we need to find the location of the config files. The location can vary depending on the way Apache was been installed. To look for the configuration we can run the following command:

apachectl -S
# or
apache2ctl -S

and look for ServerRoot

# apachectl -S                
VirtualHost configuration:
ServerRoot: "/usr/local/apache2"
...

Alternatively we can run the following command and look for the location of the bin directory.

which httpd ; which apache ; which apache2

The usual places where the configuration is stored is

/etc/apache2/
/etc/httpd/
/etc/httpd/conf
/usr/local/apache2
/opt/apache2 # usually if installed from source
C:\Program Files\Apache Software Foundation\Apache2.4\  # Windows
/usr/local/etc/httpd/  # Mac OS

The virtual host configuration can be found in the conf folder under extra/httpd-vhosts.conf or /etc/apache2/sites-available/000-default.conf or /etc/apache2/sites-available/example.conf.

Restricting a single IP

The module that supports the following directives is mod_authz_host.

These are the examples of configuration on the directory level:

<Directory "/opt/www/dir">
    ...
    <RequireAll>
        Require all granted
        Require not ip 8.8.8.8
    </RequireAll>
</Directory>

On the virtual host level:

<VirtualHost *:80>
    ...
    <Location "/">
        <RequireAll>
            Require all granted
            Require not ip 8.8.8.8
        </RequireAll>
    </Location>
</VirtualHost>

On the endpoint level:

<VirtualHost *:80>
    ...
    <Location "/login">  # notice endpoint here
        <RequireAll>
            Require all granted
            Require not ip 8.8.8.8
        </RequireAll>
    </Location>
</VirtualHost>

Managing a denylist

To store denylist in a file we can use Include directory:

<RequireAll>
    Require all granted
    Include /etc/apache2/denylist.conf
</RequireAll>

The denylist.conf can contain each directive divided by a newline.

# cat denylist.conf
Require not ip 8.8.8.8
Require not ip 9.9.9.9

Restrict multiple IPs

The directive supports providing multiple IPs along with CIDR ranges and partial IPs.

# single IP
Require not ip 8.8.8.8

# multiple IPs
Require not ip 8.8.8.1 8.8.8.2 8.8.8.3

# partial IP
Require not ip 192.168

# CIDR range
Require not ip 9.9.9.0/24

Error message

The default error message is a 403 Forbidden.

# curl localhost:8080
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
</body></html>

Applying configuration

There are many different ways to apply and reload the configuration depending on the way Apache was installed. The following example should restart the server gracefully.

apachectl configtest
# or
httpd -M
# or
apache2 -M

to test the configuration.

apachectl -k graceful
# or
/etc/init.d/httpd graceful
# or
/sbin/service httpd graceful
# or
/etc/init.d/apache2 reload
# or
sudo service apache2 reload
# or for Windows
httpd.exe -k restart
# or
apache.exe -k restart

to reload.