Permission Denied: Debugging File and Directory Permissions

01

Understanding Permission Errors

When you see "Permission denied," the first step is understanding what user is trying to access what file and what operation it's attempting. Read the full error message — it tells you exactly what's wrong.

Permission denied: /var/www/mysite/uploads/file.txt

This means: some process tried to access the file and was blocked by permissions.

02

Diagnose: Check File Permissions

View Permissions with ls

bash
ls -la /var/www/mysite/

Output example:

drwxr-xr-x 5 www-data www-data 4096 Mar 28 10:15 . -rw-r--r-- 1 www-data www-data 2048 Mar 28 10:15 index.php drwxr-xr-x 2 www-data www-data 4096 Mar 28 10:15 uploads

Understanding the Symbols

-rw-r--r-- 1 www-data www-data ^ ^ ^ ^ | | | | | | | +-- Others (everyone else) | | +------ Group | +--------- Owner +------------ File type (- = file, d = directory)

Permissions breakdown: rw-r--r--

  • Owner (user): rw- (read, write, no execute)
  • Group: r-- (read only)
  • Others: r-- (read only)
03

Identify the Process Owner

When a web request causes a permission error, find which user is running the web server:

bash
# For Nginx
ps aux | grep nginx
# Output:
# root      1234  0.0  0.1  12345   5678 ?  Ss  10:00 nginx: master process
# www-data  1235  0.0  0.2  23456   9876 ?  S   10:00 nginx: worker process

# For Apache
ps aux | grep apache
# Output:
# root      4567  0.0  0.1  45678   3456 ?  Ss  10:00 /usr/sbin/apache2
# www-data  4568  0.0  0.2  56789   4567 ?  S   10:00 /usr/sbin/apache2

# For PHP-FPM
ps aux | grep php
# Output:
# root      7890  0.0  0.1  67890   2345 ?  Ss  10:00 php-fpm: master process
# www-data  7891  0.0  0.3  78901   3456 ?  S   10:00 php-fpm: pool www

The worker processes run as www-data (or sometimes nobody, apache, nginx).

04

Standard Web Server Permissions

Recommended Permission Model

ItemPermissionOwnerGroupReason
Web root755www-datawww-dataServer needs to read/execute
Files644www-datawww-dataServer reads, users read
Directories755www-datawww-dataServer needs to list contents
Uploads755www-datawww-dataServer writes new files here
Config600www-datawww-dataSensitive data, server-only

Set Correct Permissions

Fix ownership:

bash
chown -R www-data:www-data /var/www/mysite

Fix permissions (directories):

bash
find /var/www/mysite -type d -exec chmod 755 {} \;

Fix permissions (files):

bash
find /var/www/mysite -type f -exec chmod 644 {} \;

Or as a one-liner:

bash
chmod -R 755 /var/www/mysite && find /var/www/mysite -type f -exec chmod 644 {} \;
05

Test Permissions As the Actual User

Don't just test as root. Test as the actual process user to catch permission issues:

bash
# Test if www-data can read the file
sudo -u www-data cat /var/www/mysite/config.php

# Test if www-data can write to uploads
sudo -u www-data touch /var/www/mysite/uploads/test.txt
sudo -u www-data rm /var/www/mysite/uploads/test.txt

# Test directory listing
sudo -u www-data ls -la /var/www/mysite/

If any of these fail, you've found the permission issue.

06

Common Problem Areas

1. /var/lib/php/sessions

PHP-FPM needs write access:

bash
ls -la /var/lib/php/
# Should show: drwx------ 2 www-data www-data

chmod 700 /var/lib/php/sessions
chown -R www-data:www-data /var/lib/php/sessions

2. /var/log

Application and web server logs:

bash
# Nginx
chmod 755 /var/log/nginx
chown -R root:adm /var/log/nginx

# Apache
chmod 755 /var/log/apache2
chown -R root:adm /var/log/apache2

3. /tmp and /var/run

Temporary files and sockets:

bash
ls -la /tmp | head
ls -la /var/run | head

# Should be world-writable or owned by the process user
chmod 1777 /tmp

4. Socket Files

For PHP-FPM or other services using sockets:

bash
ls -la /var/run/php-fpm.sock
# Should show: srw-rw---- 1 www-data www-data

# If wrong permissions:
chmod 660 /var/run/php-fpm.sock
chown www-data:www-data /var/run/php-fpm.sock
07

Advanced: SELinux and AppArmor

If permissions look correct but errors persist, check mandatory access control systems:

SELinux (RHEL, CentOS, Fedora)

Check if enabled:

bash
getenforce
# Output: Enforcing, Permissive, or Disabled

View SELinux context:

bash
ls -Z /var/www/mysite
# Output: -rw-r--r-- www-data www-data unconfined_u:object_r:httpd_sys_content_t:s0 file.txt

Set correct context:

bash
chcon -R -t httpd_sys_rw_content_t /var/www/mysite/uploads

Restore default contexts:

bash
restorecon -R -v /var/www/mysite

AppArmor (Ubuntu, Debian)

Check if enabled:

bash
aa-status

View loaded profiles:

bash
aa-status | grep nginx
aa-status | grep apache2

Check if AppArmor is blocking:

bash
tail -f /var/log/syslog | grep apparmor
08

ACLs (Access Control Lists)

For complex permission requirements:

View ACLs

bash
getfacl /var/www/mysite/

Set ACLs

bash
# Give www-data group write access
setfacl -m g:www-data:rwx /var/www/mysite/uploads
09

Special Permission Bits

Sticky Bit (for shared directories)

Prevents users from deleting files they don't own:

bash
# Add sticky bit
chmod +t /var/www/mysite/uploads
# Same as
chmod 1755 /var/www/mysite/uploads

# View
ls -la /var/www/mysite/
# Shows: drwxr-xr-t (note the 't')

SetUID / SetGID

Run a file as its owner (not recommended for web):

bash
# SetUID - rarely needed for web
chmod u+s /path/to/file

# SetGID - sometimes used for uploads
chmod g+s /var/www/mysite/uploads

Never use chmod 777 in production. It makes files world-writable, creating massive security risks. Always use the minimal required permissions: 755 for directories, 644 for files.

10

Real-World Troubleshooting Example

Scenario: WordPress upload fails with "Permission denied" error.

Step 1: Check who runs Nginx

bash
ps aux | grep nginx
# www-data is running Nginx

Step 2: Check uploads directory permissions

bash
ls -la /var/www/wordpress/
# drwxr-xr-x 2 root root uploads  ← WRONG: owned by root, not www-data

Step 3: Test as www-data

bash
sudo -u www-data touch /var/www/wordpress/uploads/test.txt
# touch: cannot touch '/var/www/wordpress/uploads/test.txt': Permission denied

Step 4: Fix ownership

bash
chown -R www-data:www-data /var/www/wordpress/uploads
chmod 755 /var/www/wordpress/uploads

Step 5: Test again

bash
sudo -u www-data touch /var/www/wordpress/uploads/test.txt
# Success — file created

Step 6: Upload now works

11

Quick Diagnostic Checklist

  • [ ] Read the full error message — what file, what user?
  • [ ] Check file/directory existence: ls -la /path/to/file
  • [ ] Check owner: ls -l shows owner and group
  • [ ] Check process user: ps aux | grep nginx
  • [ ] Test as process user: sudo -u www-data cat /path/to/file
  • [ ] Verify Nginx/Apache is running as correct user
  • [ ] Fix ownership: chown -R www-data:www-data /var/www/
  • [ ] Fix permissions: chmod 755 /var/www && find /var/www -type f -exec chmod 644 {} \;
  • [ ] Check SELinux/AppArmor if still failing: getenforce, aa-status
  • [ ] Review logs: /var/log/nginx/error.log, /var/log/apache2/error.log

DeluxHost, founded in 2023, offers high-quality hosting solutions for various digital needs. We provide shared hosting, VPS, and dedicated servers with advanced security and global data centers.

© DeluxHost, All rights reserved. | VAT Number : IT17734661006
All Systems Operational