Debugging Apache 403 Forbidden Error: Why “Require all granted” Fails and How to Fix It


2 views

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:

  1. First evaluates Require all denied (from some parent config)
  2. Then evaluates your Require all granted
  3. 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