When I set up a new Apache2 virtual host on Debian testing with what appeared to be correct directory permissions:
<Directory "/path/to/project">
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
I encountered a puzzling 403 Forbidden error. The logs showed something particularly confusing:
[authz_core:debug] AH01626: authorization result of Require all denied: denied
The key insight comes from understanding how Apache merges configurations. The error message suggests that somewhere else in your configuration, there's a Require all denied
that's taking precedence. This could be from:
- Parent directory directives
- Main server configuration
- .htaccess files in parent directories
- Included configuration snippets
Here's how to systematically troubleshoot:
# 1. Check merged configuration
apachectl -S
apachectl -t -D DUMP_CONFIG
# 2. Search for conflicting directives
grep -r "Require all denied" /etc/apache2/
# 3. Check virtual host inheritance
# Explicitly override any parent directives by adding:
<Directory "/path/to/project">
Require all granted
# Clear previous restrictions
<RequireAll>
Require all granted
</RequireAll>
</Directory>
Beyond the Require directive, verify these often-overlooked aspects:
# Filesystem permissions (Apache needs +x on directories)
namei -l /path/to/project
# SELinux contexts (on RHEL-based systems)
ls -Z /path/to/project
# Check for conflicting modules
apachectl -M | grep auth
Here's a tested virtual host configuration that avoids these conflicts:
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/example
<Directory "/var/www/example">
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
# Explicit authorization stack
<IfModule mod_authz_core.c>
<RequireAll>
Require all granted
</RequireAll>
</IfModule>
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
The sequence in your log reveals the authorization flow:
- First evaluates
Require all denied
(from some parent config) - Then evaluates your
Require all granted
- Finally combines them in
RequireAny
context
To see exactly which file contains the conflicting directive:
apache2ctl -t -D DUMP_CONFIG | grep -A10 -B10 "Require all denied"
When working with Apache2 configurations, seeing Require all granted
being ignored while the logs show Require all denied
can be particularly frustrating. Let's examine multiple potential causes and solutions.
# First check for conflicting directives in other config files
grep -r "Require all denied" /etc/apache2/
# Also check for older Order/Allow directives
grep -r "Order allow,deny" /etc/apache2/
Sometimes the issue stems from another configuration file overriding your settings. The Apache configuration system processes files in a specific order, and a Require all denied
in a later-loaded file could override your permissions.
# Check SELinux status if applicable
getenforce
# If enforcing, try:
chcon -R -t httpd_sys_content_t /path/to/project
semanage fcontext -a -t httpd_sys_content_t "/path/to/project(/.*)?"
Even with correct Apache directives, filesystem permissions can cause 403 errors:
# Verify ownership and permissions
ls -la /path/to/project
# Recommended settings:
chown -R www-data:www-data /path/to/project
chmod -R 755 /path/to/project
Here's a properly configured virtual host that should work:
<VirtualHost *:80>
ServerName yourdomain.com
DocumentRoot /path/to/project
<Directory "/path/to/project">
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
# For Apache 2.2 compatibility (though not recommended):
# Order allow,deny
# Allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Ensure the authorization module is loaded:
# Check if mod_authz_host is enabled
apache2ctl -M | grep authz_host
# If not, enable it:
a2enmod authz_host
systemctl restart apache2
Add these directives to your config for detailed authorization logging:
LogLevel authz_core:trace6
LogLevel authz_host:trace6