Every file and directory on Linux has permissions that decide who can read, write and execute it. chmod (change mode) sets them. Getting them right is the difference between a working, secure web server and “403 Forbidden” — or a site anyone can deface.
Reading permissions
ls -l
-rw-r--r-- 1 www-data www-data 4096 Nov 4 10:00 index.php
drwxr-xr-x 2 www-data www-data 4096 Nov 4 10:00 uploads
-rwxr-x--- 1 root deploy 812 Nov 4 10:00 deploy.sh
The first column breaks down as:
- rw- r-- r--
│ │ │ └── others (everyone else)
│ │ └─────── group
│ └──────────── user (owner)
└──────────────── type: - file, d directory, l link
What r, w and x mean
| File | Directory | |
|---|---|---|
| r (read) | Read the contents | List the names inside |
| w (write) | Modify the contents | Create, delete, rename entries inside |
| x (execute) | Run it as a program | Enter it (cd) and access files inside |
The directory rules surprise people:
- To read
/var/www/site/index.html, a user needs x on every directory in the path, plus r on the file. - Deleting a file depends on w on the directory, not on the file itself.
Numeric (octal) modes
Each permission has a value: r = 4, w = 2, x = 1. Add them per class:
| Number | Permissions |
|---|---|
| 7 | rwx |
| 6 | rw- |
| 5 | r-x |
| 4 | r– |
| 0 | — |
Three digits = user, group, others:
chmod 644 index.php # rw-r--r-- normal files
chmod 755 uploads # rwxr-xr-x normal directories, scripts
chmod 600 ~/.ssh/id_ed25519 # rw------- private keys, secrets
chmod 700 ~/.ssh # rwx------ private directories
chmod 640 wp-config.php # rw-r----- owner writes, group (web server) reads
chmod 750 deploy.sh # rwxr-x--- owner and group may run it
Symbolic modes
Change specific bits without restating everything:
chmod u+x script.sh # add execute for the owner
chmod g-w file # remove write from the group
chmod o-rwx secret.txt # remove everything for others
chmod a+r file # add read for all (user, group, others)
chmod u=rw,go=r file # set exactly: same as 644
chmod +x script.sh # execute for everyone (respecting umask)
u user, g group, o others, a all; + add, - remove, = set exactly.
Recursive changes — carefully
chmod -R 755 /var/www/site makes every file executable, which is wrong for files. Treat files and directories separately with find:
find /var/www/site -type d -exec chmod 755 {} +
find /var/www/site -type f -exec chmod 644 {} +
Or use the capital X, which adds execute only to directories (and files already executable by someone):
chmod -R u=rwX,go=rX /var/www/site
Why chmod 777 is (almost) never the answer
777 gives everyone on the system full control. On a web server, that means any compromised process — another site, a vulnerable plugin — can modify your files and plant malware. When something “only works with 777”, the real problem is ownership: the web server user doesn’t own the directory it needs to write to. Fix that with chown instead:
sudo chown -R www-data:www-data /var/www/site/wp-content/uploads
sudo chmod 755 /var/www/site/wp-content/uploads
Recommended web permissions
| Path | Mode | Notes |
|---|---|---|
| Directories | 755 | or 750 if others needn’t access |
| Files | 644 | or 640 |
Config with secrets (wp-config.php, .env) |
640 or 600 | readable by the app only |
| Upload/cache directories | 755, owned by the web server user | |
| Scripts | 750 |
See also WordPress file permissions.
SSH is strict about permissions
OpenSSH refuses keys with loose permissions, with errors like UNPROTECTED PRIVATE KEY FILE! or a silent fallback to password login:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_*
chmod 644 ~/.ssh/*.pub
Your home directory must not be group- or world-writable either. Details in SSH “Permission denied (publickey)”.
Special bits: setuid, setgid, sticky
A fourth, leading digit sets special permissions:
chmod 2775 /srv/shared # setgid on a directory: new files inherit the group
chmod 1777 /tmp # sticky bit: users can delete only their own files
chmod 4755 /usr/bin/passwd # setuid: runs as the file's owner (root)
The setgid directory is handy for team folders. Setuid binaries are a security risk — audit them with find / -perm -4000 -type f. More in setuid, setgid and sticky bit.
Default permissions: umask
New files get permissions from the umask, typically 022 → files 644, directories 755. umask 027 gives 640/750. See umask explained.
Useful options
chmod -v 644 file # verbose: print each change
chmod -c -R u+rwX dir # print only files actually changed
chmod --reference=a.txt b.txt # copy a.txt's mode to b.txt
stat -c '%a %n' * # show numeric modes
Learn the full model in Linux file permissions explained. The safest place to practise is a scratch server — any Linux VPS can be reinstalled in minutes if you break something.