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
- Verify parent directory permissions with
namei -l /xyz/www
- Check Apache error logs:
tail -f /var/log/httpd/error_log
- Test with SELinux disabled:
setenforce 0
- 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:
- Verify
User
/Group
in httpd.conf owns the files - Confirm parent directories have +x permission
- Check SELinux contexts for DocumentRoot
- Inspect audit logs for AVC denials
- Test with SELinux in permissive mode (
setenforce 0
)