AVC denial or Permission denied in /var/log/audit/audit.log

SELinux blocking Apache from writing to /var/www/html

Linux & Unix Intermediate 👁 10 views 📅 Jul 2, 2026

When SELinux denies Apache write access to a file, you get a vague 'Permission denied'. This fix shows how to see the real issue and fix it with the right SELinux contexts.

When does this happen?

You set up a web app on Apache, maybe a CMS like WordPress or a custom PHP script. You give the /var/www/html/uploads folder 777 permissions. But when you try to upload a file, you get a generic "Permission denied" error. The error log in /var/log/httpd/error_log just says something like client denied by server configuration or plain Permission denied.

Had a client last month whose entire print queue died because of this — no, wait, different story. But I've seen this exact thing with Drupal and WordPress sites on CentOS 7 and RHEL 8. The Apache process runs as user apache, and the folder permissions look fine, but SELinux is blocking the write.

Root cause: SELinux context mismatch

SELinux controls what processes can access files based on labels (contexts). Apache's process (httpd_t) by default can only write to files labeled httpd_sys_rw_content_t. If you create a folder under /var/www/html using mkdir or FTP, it gets the default label for that location, which is httpd_sys_content_t — read-only. That's why Apache can read files but not write to them, even if Unix permissions are wide open.

The fix isn't to disable SELinux (that's lazy and insecure). You change the file's SELinux context or adjust the boolean that allows Apache to write to specific locations.

How to fix it

First, confirm it's SELinux. Check the audit log:

sudo grep avc /var/log/audit/audit.log | tail -5

If you see lines with denied and httpd_t, that's your culprit. You can also use audit2why to get a plain English explanation:

sudo audit2why < /var/log/audit/audit.log | head -20

Now, here are the numbered steps to fix it:

  1. Install the policycoreutils tools if not already there:
    sudo yum install policycoreutils policycoreutils-python (or dnf on newer systems).
  2. Find the folder that needs write access. Let's say it's /var/www/html/uploads. Check its current context:
    ls -Z /var/www/html/uploads
    You'll see something like system_u:object_r:httpd_sys_content_t.
  3. Change the context to writable. Run:
    sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/uploads(/.*)?"
    This tells SELinux to remember the change even after a relabel.
  4. Apply the new context to the folder now. Run:
    sudo restorecon -Rv /var/www/html/uploads
  5. Test the write. Create a file as the Apache user:
    sudo -u apache touch /var/www/html/uploads/test.txt
    If no error, it's fixed.

If that still fails, maybe you need a broader boolean. For example, if Apache needs to write to /var/www/html itself (not a subfolder), you might need httpd_unified boolean:

sudo setsebool -P httpd_unified 1

But I recommend using the specific context instead — it's more secure and you won't accidentally open the whole system.

What to check if it still fails

Sometimes the folder is at a non-standard location, like /home/user/public_html. In that case, you also need to make sure the httpd_t process has access to /home directories. Run:

sudo setsebool -P httpd_read_user_content 1

Also check if the error is actually from a different cause. Look at /var/log/httpd/error_log for things like open_basedir restriction in PHP or a RewriteRule that blocks the path. SELinux denials show up clearly in audit.log though, so that's your first stop.

If you're using Apache with mod_rewrite or PHP-FPM, sometimes the user context changes. For PHP-FPM running as a different user, you need httpd_execmem or httpd_tty_comm booleans. But 90% of the time, the fix above is all you need.

Was this solution helpful?