SELinux Denying Web App Access: Fix 'Permission Denied' on /var/www/html
Apache can't read files in /var/www/html even with 755 permissions. The fix is adding a file context label, not disabling SELinux.
You set up Apache on CentOS 7 or RHEL 8. You copied your website files to /var/www/html with chmod 755. But when you browse to the site, you get a 403 Forbidden or a Permission Denied error in the Apache error log. The trigger is usually after you rsync or cp files from another directory, like cp -r /home/user/my-site/* /var/www/html/. The files have the right permissions, but SELinux is blocking Apache from reading them.
Root Cause
SELinux labels every file and process with a context. Apache runs under the httpd_t domain. It can only read files labeled httpd_sys_content_t or httpd_sys_rw_content_t. When you copy files from a user home directory, they keep the user_home_t label. Apache can't touch those. You can check the current label with ls -Z /var/www/html/index.html. You'll see something like system_u:object_r:user_home_t instead of httpd_sys_content_t.
The Fix
Do not run setenforce 0. That disables SELinux and breaks the security model. The real fix is to restore or change the file context.
Step 1: Check the current SELinux context
ls -Z /var/www/html/index.html
You should see a context like unconfined_u:object_r:user_home_t. This confirms the label is wrong.
Step 2: Restore the default context for the whole directory
sudo restorecon -Rv /var/www/html
The -R flag goes into subdirectories. The -v shows what it changes. After this, run ls -Z /var/www/html/index.html again. You should see system_u:object_r:httpd_sys_content_t. This usually fixes the problem.
Step 3: If you need a custom path (like /opt/myweb)
sudo semanage fcontext -a -t httpd_sys_content_t '/opt/myweb(/.*)?'
sudo restorecon -Rv /opt/myweb
The first command tells SELinux to label anything under /opt/myweb as httpd_sys_content_t. The restorecon applies the change immediately. You only do this once per path.
Step 4: If Apache needs to write files (uploads, logs)
Use a different context: httpd_sys_rw_content_t for read-write. But be careful — this lets Apache modify files. Only do this for specific directories, like an upload folder.
sudo semanage fcontext -a -t httpd_sys_rw_content_t '/var/www/html/uploads(/.*)?'
sudo restorecon -Rv /var/www/html/uploads
Step 5: Check SELinux booleans
Sometimes Apache needs extra permissions to access home directories or use CGI scripts. Use getsebool -a | grep httpd to see all Apache-related booleans. Common ones:
httpd_read_user_content— lets Apache read files in user home dirs. Set it withsudo setsebool -P httpd_read_user_content on.httpd_enable_homedirs— enables UserDir support.httpd_can_network_connect— allows Apache to make outbound connections (like to a database).
The -P flag makes the change permanent across reboots.
What to Check If It Still Fails
First, look at the SELinux audit log. Run sudo ausearch -m avc -ts recent. This shows the exact denial. The output tells you the process name (httpd_t), the target file, and the missing permission. You might see something like denied { read } for pid=1234 comm="httpd" name="index.html". That confirms you fixed the wrong file or missed a subdirectory.
Second, check if the file is a symlink. SELinux can follow symlinks, but the target file must also have the right context. Run ls -Z -l /var/www/html/index.html. If it points to /home/user/site/index.html, label that file too with restorecon.
Third, if you're running a web app like WordPress or Drupal, it might need write access to /var/www/html/wp-content. Set that directory to httpd_sys_rw_content_t as shown in Step 4. Also check SELinux booleans for database connections or sending emails.
Last resort — check the Apache error log: tail -f /var/log/httpd/error_log. It often says Permission denied: /var/www/html/index.html. If SELinux is still blocking, you'll also see an AVC denial in /var/log/audit/audit.log. If you don't see any AVC messages, the problem is not SELinux — check file permissions or Apache config.
Was this solution helpful?