Short version: use 755 for directories and scripts you run, 644 for ordinary files like HTML, CSS, images and config. If you want to see any number translated, use the chmod Calculator.
How to read the number
Each digit is a sum of three values:
| Permission | Value | Letter |
|---|---|---|
| read | 4 | r |
| write | 2 | w |
| execute | 1 | x |
The three digits are for owner, group and others, in that order.
| Number | Owner | Group | Others | ls -l shows |
|---|---|---|---|---|
| 755 | rwx (7) | r-x (5) | r-x (5) | rwxr-xr-x |
| 644 | rw- (6) | r-- (4) | r-- (4) | rw-r--r-- |
| 700 | rwx | --- | --- | rwx------ |
| 600 | rw- | --- | --- | rw------- |
| 777 | rwx | rwx | rwx | rwxrwxrwx |
So 7 = 4 + 2 + 1, 5 = 4 + 1, 6 = 4 + 2.
What "execute" means on a directory
On a file, x means you can run it as a program.
On a directory, x means you can enter it and reach the files inside. Without it, cd fails and the web server can't serve anything below that folder, even if the files themselves are 644. That's why directories need 755 and files only need 644.
Which permission to use
| What | Permission |
|---|---|
| Directories | 755 |
| Web files (HTML, CSS, JS, images) | 644 |
| Shell scripts and binaries you run | 755 |
Config containing secrets (.env, wp-config.php) | 600 or 640 |
SSH private key (~/.ssh/id_ed25519) | 600 |
~/.ssh directory | 700 |
| Shared upload folder, group-writable | 775 (with the right group) |
Why 777 is the wrong fix
The usual story: the app can't write to a folder, someone runs chmod -R 777, the error goes away. What actually happened is that every user and process on the machine can now replace your files. If any part of the server is compromised, the attacker can rewrite your code.
The real fix is usually ownership. Find out which user the process runs as and give that user the folder:
ps aux | grep -E 'nginx|php-fpm|node' # see the process user sudo chown -R www-data:www-data /var/www/app/storage sudo chmod -R 775 /var/www/app/storage
Setting permissions on a whole project
Don't run chmod -R 755 on everything. That makes every file executable. Set directories and files separately:
find /var/www/site -type d -exec chmod 755 {} + find /var/www/site -type f -exec chmod 644 {} +
Then make the scripts that need it executable:
chmod 755 /var/www/site/bin/deploy.sh
Symbolic mode
You don't have to use numbers. Symbolic mode changes one thing without touching the rest:
chmod +x script.sh # add execute for everyone chmod u+x script.sh # add execute for the owner only chmod go-w file.txt # remove write from group and others chmod o= secret.env # remove all permissions for others
Git and permissions
Git tracks only the execute bit. If a script loses it after a clone:
git update-index --chmod=+x scripts/deploy.sh git commit -m "Make deploy script executable"
If Git shows every file as modified after copying between machines, it's usually mode changes. git config core.fileMode false stops Git tracking them in that repo.