Back to problems

problem hub

Read-only first

Linux permission denied

Trace owner, mode, ACL, symlink, and parent-directory permissions before recursive chmod or chown.

Safest first command

namei -l /var/www/example/index.html

Before you run this

Expected output: A path-by-path permission chain showing each parent directory and the final file.

When not to use it: Do not use broad recursive chmod or chown as a first response. It can expose files or break services.

Expected output example

f: /var/www/example/index.html
drwxr-xr-x root root /
drwxr-xr-x root root var
drwxr-x--- root www-data www
-rw-r----- deploy www-data index.html

How to read the result

Every parent directory needs execute permission for the user or service that traverses it. The final file also needs the needed read/write/execute bit for that access path.

Trace the whole path

A readable file can still fail if a parent directory lacks execute permission. Start with the path chain.

  1. namei -l /var/www/example/index.html
  2. stat -c '%A %U:%G %n' /var/www/example/index.html

Check symlinks and special bits

Symlinks, setuid bits, sticky directories, and ACLs can make a simple mode display misleading.

Common causes

  • Missing execute bit on a parent directory
  • Wrong owner or group after deploy
  • ACL overriding simple mode bits
  • Symlink target with stricter permissions
  • SELinux/AppArmor denial even when Unix mode looks correct

What not to change yet

  • Do not run chmod -R 777.
  • Do not chown a whole web root without knowing the service user.
  • Do not change symlink targets before checking the full path chain.

platform notes

Distro and service notes

Web servers

Nginx/Apache usually read as www-data, nginx, apache, or a configured service user.

macOS

BSD stat/namei behavior differs; use macOS-specific pages for Apple Terminal workflows.

SELinux/AppArmor

If modes look correct, check mandatory access-control logs before broad chmod/chown.

supporting commands

Command path