Apache Web Server Access Control with mod_authz_core Require Directive and Authorization Providers
The Apache HTTP Server is meant to allow access to a bunch of defined resources that clients request over the public or private networks. Now we may not want to expose certain resources over these networks. In fact at a granular level, we may want to authorize access to resources based on various whitelisted hosts, IP addresses, environment variables, HTTP methods, time of the day, server expressions, etc. The mod_authz_core
module allows us to do just that. It provides certain directives (chiefly Require
) that comes with authorization capabilities allowing us to control the access of authenticated/non-authenticated clients to different resources or portions of the website.
Require Directive
The Require
directive that goes into your configuration (either in the main server configuration file or .htaccess
) tests if a client is authorized to access a resource based on the restrictions imposed by the authorization provider specified, and its arguments. Here’s how the syntax looks like:
Require [not] authorization-provider arg [arg] …
An authorization provider is the actual thing that allows enforcing access restrictions. Think of it as a piece of code, a function or a module that accepts a bunch of arguments and lets you control access to your web resources under certain conditions. Don’t worry too much, the majority of this article will be covering various kinds of authorization providers and you’ll find out its nothing super complex.
The Require
directive can be specified within the <Directory>
, <Files>
or <Location>
sections of the main server configuration files or the .htaccess
file. To make sure no mistakes are made, let’s look at a pseudo-snippet:
... lots of config ...
<VirtualHost *:80>
...
<Directory /var/www>
...
# Allow access to all clients/users
Require all granted
...
</Directory>
...
<Location /special-section>
...
# Allow access to only authenticated users
Require valid-user
...
</Location>
...
</VirtualHost>
... lots of config ...
It is important to remember that some authorization providers work on a normal client whereas some of them work on users authenticated via HTTP Basic auth. Any further mention of “authenticated users” in this article would hence refer to the latter.
Let’s go through the different authorization providers provided by various modules then.
mod_authz_core
The mod_authz_core
module provides us with some generic authorization providers besides providing the Require
directive itself. It also provides the functionality to register other authorization providers. This functionality is well used by a lot of other modules (as you’ll see in the sections to follow) to register their own set providers to be used with the Require
directive. Let’s go through the generic providers first that this module ships with.
Require all
The all
provider allows or denies access unconditionally.
# Allow access unconditionally
Require all granted
# Deny access unconditionally
Require all denied
When you set up a brand new virtual host for your site, you’ll notice by default httpd
doesn’t really let any kind of client access your website. It throws a 403 Forbidden
page.

To solve for this you must put a Require all granted
in your vhost in the context of a directory or location.
Require env
The env
provider allows access if one of the environment variables provided in the arguments list is set. You can pass more than one environment variables to the provider.
# Syntax: Require env env-var [env-var] ...
# Only allow access to internal bots
SetEnvIf User-Agent "Internalbot" internal_bot
<Directory "/docroot">
Require env internal_bot
</Directory>
We set an environment variable called internal_bot
(using SetEnvIf
) only if the clients’ user agent string contains the term “Internalbot” (regexp). Based on that, we allow access.
Require method
The method
provider allows access for specified HTTP request types.
# Allows GET, HEAD, POST and OPTIONS requests only
Require method GET POST OPTIONS
Note: Specifying GET automatically includes HEAD as well.
Require expr
The expr
provider allows authorization based on arbitrary ap_expr
expressions. Expressions allow evaluating conditions based on checking variables, functions against values using binary, unary operators. If you go through the entire ap_expr
documentation link, you’ll realise you can perform a plethora of checks. Few examples from the documentation itself:
# Allow access between business hours 9-5 (both inclusive)
Require expr "%{TIME_HOUR} -ge 9 && %{TIME_HOUR} -le 17"
# Allow access if query string has a “secret” substring and requested URI is one of the values
Require expr "!(%{QUERY_STRING} =~ /secret/) && %{REQUEST_URI} in { '/example.cgi', '/other.cgi' }"
# Allow access if the user agent is not "BadBot"
Require expr "%{HTTP_USER_AGENT} != 'BadBot'"
mod_authz_user
The mod_authz_user
module provides additional authorization providers for the authenticated users (HTTP Basic auth), extending capabilities of the core.
Require user
This user
provider lets you specify a list of authenticated users who’d be allowed access.
Require user foo john mike
Require valid-user
The valid-user
provider just allows access to any successfully authenticated user.
Require valid-user
mod_authz_host
The mod_authz_host
extends the authorization types with further host-related providers.
Require ip
The ip
provider allows authorization based on the IP address of the remote connecting client. Of course more than one IP address can be passed as arguments. Let’s see a few examples right off the docs:
# A full IP (private or public) match
Require ip 10.1.2.3
# A partial IP check
Require ip 10 172.20 192.168.2
# Network/netmask pair
Require ip 10.1.0.0/255.255.0.0
# In the CIDR notation
Require ip 10.1.0.0/16
# IPv6 addresses and subnets
Require ip 2001:db8::a00:20ff:fea7:ccea
Require ip 2001:db8:1:1::a
Require ip 2001:db8:2:1::/64
Require ip 2001:db8:3::/48
Require host
The host
provider authorizes clients based on their host names. It does a double reverse DNS lookup to ensure that the original IP matches the specified hostname(s). In a double reverse DNS, an IP to hostname lookup is done (reverse), followed by a hostname to IP (forward) for double checking.
Require host .net example.org
The hostnames resolved from the IP must end in one of the value strings specified above (.net
or example.org
). It is important to understand that complete components are matched, hence example.org
will match foo.example.org
but not fooexample.org
.
Require forward-dns
The forward-dns
provider allows access based on simple DNS checks. The IP of the connecting client must match with the DNS records for the specified hostname(s).
Require forward-dns foo.example.com
Require local
The local
provider is the simplest of all host providers. It only allows localhost
to access resources. So either of the following two conditions must apply:
- The client address must match
127.0.0.8
or::1
. - The client and server address must be the same.
Require local
If your setup has a bunch of proxies between the client and Apache server, then the client IP address resolved at Apache’s end will be that of the reverse proxy. Hence, the Require
directives may not work as expected. In order to solve this, you’ll need to make use of mod_remoteip
that lets the server resolve to actual client IP out of the IP address list passed by the intermediate proxies (or load balancers).
mod_authz_groupfile
The mod_authz_groupfile
allows group authorization using plain text files. For this we’ll need to first create a file and define groups mapped to a list of authenticated users.
Group Files
Let’s create a group file in the server root and throw in some contents in it:
# cat /etc/apache2/groupfile.txt
mygroup: bob amy mike
mygroup2: foo bar bob
We just defined two groups called mygroup
and mygroup2
and assigned 3 users that must be authenticated before authorization, to each. With the group file in hand, we can now define our group-based authorization rules.
Require group
The group
provider accepts a bunch of group names as arguments to which the authenticated user must belong according to the mapping stored in the group file. Along with the provider, we also must tell httpd
where to look for the group file. This is done with the AuthGroupFile
directive.
AuthGroupFile groupfile.txt
# Authenticated user must belong to mygroup (should be bob, amy or mike), but not mygroup2
Require group mygroup
The file path passed to AuthGroupFile
must either be an absolute path or one relative to the server root.
mod_authz_owner
The mod_authz_owner
module authorizes access to authenticated users whose username or group name matches with the operation system owner or group of the requested file respectively. This module only takes effect when a specific file is requested like:
http://yoursite.com/index.php
http://yoursite.com/media/foo.png
http://yoursite.com/docs/readme.txt
It won’t work for access to http://yoursite.com/
or http://yoursite.com/media
even if they have a DirectoryIndex
specified. DirectoryIndex <file-name>
specifies the file that’ll be processed when the directory is requested instead of a particular file (inside that directory) directly. It is mostly set to index.html
or index.php
.
Require file-owner
The file-owner
provider only allows access to files where the name of the authenticated user is the same as that of the operating system user that owns the file.
Require file-owner
Require file-group
The file-group
provider only allows access to files where the authenticated user is a member of a group in the group database that has the same name as the operating system group of the file. This group database should be a text file as we saw in the mod_authz_groupfile
section above.
AuthGroupFile groupfile.txt
Require file-group
If you group file is huge then consider using mod_authz_dbm
(next section) as it is more efficient for looking up username/password pairs.
mod_authz_dbm
The mod_authz_dbm
module is totally similar to mod_authz_groupfile
above except instead of a plain text file, it looks up the group in a dbm
key/value database file which is a lot more performant than searching through a plain text file. Once the group is found, the authorization for a particular request happens against that.
DBM Group Files
In the dbm
database files which essentially stores key/value pairs, the keys must be the username whereas value must be a comma-separated list of groups to which the corresponding user belongs. Creating the file is easy. Apache ships with a tool called htdbm
that can be used to create and update DBM file formats. Let’s create one in our server root.
# htdbm -c -p groupdb username_foo
New password:
Re-type new password:
Database groupdb created.
username_foo
will be the authenticated user name and the password you type is a list of comma-separated groups. The -p
option is important to store the password as plain text because otherwise it’ll get encrypted and the directives below won’t really work. The thing is, htdbm
is used for maintaining a database of passwords for modules like mod_authn_dbm
. But in this case, we’re using it to create a simple dbm database file with plaintext key and values (no encryption).
There’s also the httxt2dbm
tool that ships with the server that allows conversion of text files into dbm
files and aren’t specifically meant for maintaining password-based databases. If you want to use that instead then just create a file with key value
pairs separated by a space, spanning over multiple lines if required, and pass it through this tool.
# cat groupdb.txt
username_foo mygroup,mygroup2
# httxt2dbm -v -i groupdb.txt -o groupdb
With a dbm file in place, we can get onto using the relevant directives provided by this module.
Require dbm-group
The dbm-group
provider allows access if the authenticated user belongs to the group specified by the dbm
database. In the configuration we need to provide the location of the dbm
file with the AuthDBMGroupFile
directive that accepts an absolute path or one relative to the server root.
AuthDBMGroupFile "groupdb"
Require dbm-group mygroup
Require dbm-file-group
The dbm-file-group
directive is similar to file-group
provided by mod_authz_owner
(seen above) in the sense that it authorizes access to specific files only if the authenticated user belongs to a group from the dbm
database that matches with the operation system group of the file.
AuthDBMGroupFile "groupdb"
Require dbm-file-group
Others
There are other modules that provide more authorization providers like:
mod_authz_ldap
– HTTP Basic auth over LDAP.mod_authz_dbd
– Authentication and authorization against relation databases.
I won’t cover the providers for these modules as in order to understand them, the authentication implementation must also be understood which is beyond the scope of this article.
Next up, we’ll cover some directives provided by mod_authz_core
that can be used to combine multiple Require
directives together to express complex authorization logic.
<RequireAll> Directive
The <RequireAll>
directive can be used to contain a bunch of authorization directives that must all succeed (and none must fail) in order for the <RequireAll>
block itself to succeed. It must be placed within <Directory>
, <Files>
, <Location>
or .htaccess
.
<RequireAll>
Require method GET, POST, OPTIONS
Require local
Require file-owner
</RequireAll>
The block above would allow access if the request HTTP method is GET
, HEAD
, POST
or OPTIONS
and the client is localhost
and the resource being requested is a file where the system owner of the file is same as the authenticated user.
<RequireAny> Directive
With the <RequireAny>
directive, out of the group of authorization directives that it encloses, only one must succeed in order for the entire <RequireAny>
directive to succeed. It must be placed within <Directory>
, <Files>
, <Location>
or .htaccess
. So if we re-write the example from the previous section:
<RequireAny>
Require method GET, POST, OPTIONS
Require local
Require file-owner
</RequireAny>
This block would authorize access if the request method is GET
, HEAD
, POST
or OPTIONS
or the client is localhost
or the system owner of the file being requested is the same as the authenticated user.
<RequireNone> Directive
The <RequireNone>
directive contains a group of authorization directives where none must succeed in order for the entire block to succeed (and not fail). It must be placed within <Directory>
, <Files>
, <Location>
or .htaccess
. So modifying the example from the previous section:
<RequireNone>
Require method GET, POST, OPTIONS
Require local
Require file-owner
</RequireNone>
The request must not be GET
, HEAD
, POST
or OPTIONS
(can be PUT
, DELETE
) and must not be from localhost
(can be from a public IP) and if a file is being requested then the system owner of that should not match with the authenticated user. Hence, if none of the Require
directives succeeds, then only the entire block will return a successful result and Apache will serve the content for the virtual host.
Here’s a fairly complex example from the documentation to blow your mind:
<Directory "/www/mydocs">
<RequireAll>
<RequireAny>
Require user superadmin
<RequireAll>
Require group admins
Require ldap-group "cn=Administrators,o=Airius"
<RequireAny>
Require group sales
Require ldap-attribute dept="sales"
</RequireAny>
</RequireAll>
</RequireAny>
<RequireNone>
Require group temps
Require ldap-group "cn=Temporary Employees,o=Airius"
</RequireNone>
</RequireAll>
</Directory>
The authorization logic expressed by the block is that (let A = the authenticated user
):
(
(
A’s username must be `superadmin`
)
or (
(A must belong to the `admins` group and the `Administrators` LDAP group)
and (A must belong to the `sales` group or have the LDAP `dept` attribute `sales`)
)
and (
A must not belong to the `temps` group and the `Temporary Employees` LDAP group
)
)
Require not
All the Require
directive usage we saw above can also be negated using Require not
. So for instance if Require local
means authorize access to resources only if the request is from localhost, Require not local
can negate that meaning allowing access from only non-localhost. The not
negation can be applied in all the cases we’ve seen till now.
It is important to understand though that Require not
cannot be used by itself to allow or deny a request. It must be used with another element that evaluates to true
or false
within a <RequireAll>
directive.
<RequireAll>
Require all granted
Require not ip a.b.c.d
</RequireAll>
You aren’t allowed to put Require not
inside <RequireAny>
or <RequireNone>
.
.htaccess
If you want to put any of the directives that we learnt till now in .htaccess
to make changes at the directory level or because you don’t have root access to the system, then all you have to do is create the .htaccess
file and put the contents as it is. No changes required. For instance using the dbm-group
authorization provider below:
# cat /home/usr/myapp/.htaccess
AuthType basic
AuthName "private area"
AuthBasicProvider file
AuthUserFile htpasswd.txt
AuthDBMGroupFile groupdb
Require dbm-group mygroup
The code above enables basic auth and restricts access to mygroup
group that should be looked up in the groupdb
dbm file.
For these directives to take effect, the server must allow overriding with the AllowOverride
directive:
# Inside your main conf's <Directory>
AllowOverride AuthConfig
You can get it done yourself if you have root access or ask the sysadmin.
Common UseCases
Let’s go through a few common use cases or just questions people struggle with in the beginning.
My brand new Virtual Host doesn’t work. Returns 403 Forbidden permission denied, why ?
By default, the Apache HTTP Server denies access to all resources in a virtual host. You’ll need to add the following:
Require all granted
I want to lock down access to just localhost.
That’s pretty straightforward, we’ve seen this before.
Require local
Do not specify different IPs like Require ip 127.0.0.1
because an IPv6 (::1
) access would fail then. Require local
takes care of all things local. Go back to the mod_authz_host
section above if you’ve forgotten.
How to block an IP ?
Simple, with a negation.
<RequireAll>
Require all granted
Require not ip <IP_TO_BLOCK>
</RequireAll>
How to allow access only if a header is present in the request with a non-empty value ?
Let’s say you always want the X-Auth-Token
header to be passed with a valid value, this will do the job:
Require expr "-n %{HTTP:Authorization}"
More on Apache expressions.
How to setup authentication and authorization for different users to different directories ?
This should do the trick (in your httpd.conf
or apache2.conf
):
<Directory /home/foo/myapp/dir1>
AuthName "Restricted"
AuthType Basic
AuthBasicProvider file
AuthUserFile htpasswd
Require user1 user2 user3
</Directory>
<Directory /home/foo/myapp/dir2>
AuthName "Restricted"
AuthType Basic
AuthBasicProvider file
AuthUserFile htpasswd
Require user3 user4 user5
</Directory>
Authorize authenticated user access to all pages except a specific one (or vice-versa).
<Directory /home/foo/myapp>
AuthName "Restricted"
AuthType Basic
AuthBasicProvider file
AuthUserFile htpasswd
Require valid-user
</Directory>
# Page with no authentication, no authorization
<Location /foo.php>
Require all granted
</Location>
Conclusion
We just learnt a plethora of authorization providers provided by various httpd
modules to be used with the Require
directive to impose access restrictions on resources. They can all be mixed up in a group as well to specify a more complex logic within <RequireAll>
, <RequireAny>
and <RequireNone>
sections. Hopefully in the future, referring to this page will let you whip up your complex set of authorization configurations.
If you want to read up on Apache Web Server Authentication modules, then read my other article.