How to Fix Apache HTTPD 403 “Permission denied: Can’t open directory” Error on DocumentRoot


4 views

When Apache throws the error (13)Permission denied: Can't open directory for index: /xyz/www/, it's typically a multi-layered permissions issue that goes beyond simple chmod 777. Here's what's really happening under the hood:

# These are the critical permission points to check
# 1. Directory traversal permissions
namei -l /xyz/www/
# 2. SELinux context (if enabled)
ls -Z /xyz/www/
# 3. Apache user privileges
ps aux | grep httpd

The execute bit (x) on directories is often misunderstood. For Apache to serve content:

# Minimum required permissions:
chmod 755 /xyz
chmod 755 /xyz/www
chown -R apache:apache /xyz/www  # Or www-data:www-data on Debian

# Verify with:
namei -l /xyz/www/index.html

On RHEL/CentOS systems, SELinux often blocks access even with 777 permissions:

# Check SELinux status:
sestatus

# Temporary solution (for testing):
setenforce 0

# Permanent solution:
chcon -R -t httpd_sys_content_t /xyz/www/
semanage fcontext -a -t httpd_sys_content_t "/xyz/www(/.*)?"
restorecon -Rv /xyz/www

Your Directory block needs modern syntax for newer Apache versions:


    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted  # Replaces old Order/Allow directives
    
    # For PHP files:
    
        SetHandler application/x-httpd-php
    

  1. Verify parent directory permissions with namei -l /xyz/www
  2. Check Apache error logs: tail -f /var/log/httpd/error_log
  3. Test with SELinux disabled: setenforce 0
  4. Verify Apache user can list directories: sudo -u apache ls -l /xyz/www

When setting up a new Apache DocumentRoot, the first instinct is to check filesystem permissions - and that's exactly what I did when encountering:

(13)Permission denied: Can't open directory for index: /xyz/www/

But here's what most tutorials don't tell you: On modern Linux systems (especially RHEL/CentOS/Fedora), SELinux contexts matter just as much as traditional permissions.

First, let's verify the basic requirements. Apache needs:

# Minimal directory permissions
chmod 755 /xyz/www
# Files should be readable by others
chmod 644 /xyz/www/*

But wait - if you've already tried chmod -R 777 and it still fails, we need to look deeper.

Check current SELinux context:

ls -Z /xyz/www
# Should show something like:
# system_u:object_r:httpd_sys_content_t:s0 /xyz/www

If it's not set to httpd_sys_content_t, fix it with:

semanage fcontext -a -t httpd_sys_content_t "/xyz/www(/.*)?"
restorecon -Rv /xyz/www

For non-standard directory locations, we might need:

# For directories containing executable scripts:
chcon -t httpd_sys_script_exec_t /xyz/www/cgi-bin

# For upload directories:
chcon -t httpd_sys_rw_content_t /xyz/www/uploads
setsebool -P httpd_unified 1

Check SELinux denials in real-time:

tail -f /var/log/audit/audit.log | grep AVC

Or generate human-readable reports:

ausearch -m avc -ts recent | audit2why

While debugging, temporarily enable more verbose logging:

<Directory "/xyz/www">
    LogLevel debug
    Require all granted
</Directory>

Remember to revert this in production!

When facing 403 errors:

  1. Verify User/Group in httpd.conf owns the files
  2. Confirm parent directories have +x permission
  3. Check SELinux contexts for DocumentRoot
  4. Inspect audit logs for AVC denials
  5. Test with SELinux in permissive mode (setenforce 0)