chmod 755 vs 644 Explained: Linux File Permissions Without the Mystery
Why 755 for directories and executables, 644 for everything else, and the setuid trap that bit me in production. Linux file permissions explained with the octal-to-symbolic map that finally made it click.
chmod 755 vs 644 Explained: Linux File Permissions Without the Mystery
For my first three years on Linux I copy-pasted chmod 755 and chmod 644 from Stack Overflow and never really understood why those particular numbers. Then a server I owned got compromised through a world-writable .htaccess file, and I sat down to actually learn the model. Took about 30 minutes. Should have done it sooner.
This post is the version I wish I'd read in year one.
The Three Trios
Every file on Linux has nine permission bits, arranged in three groups of three:
owner group others
rwx rwx rwx
Each trio answers: can you read it, write to it, execute it (or, for a directory, enter it)?
That's it. The rest is notation.
Octal: Where the Numbers Come From
Each trio is three bits, which is a number 0–7. Read = 4, write = 2, execute = 1. Add them up:
| rwx | binary | octal | |-----|--------|-------| | --- | 000 | 0 | | --x | 001 | 1 | | -w- | 010 | 2 | | -wx | 011 | 3 | | r-- | 100 | 4 | | r-x | 101 | 5 | | rw- | 110 | 6 | | rwx | 111 | 7 |
So 755 reads left to right: owner gets 7 (rwx), group gets 5 (r-x), others get 5 (r-x).
644 is: owner 6 (rw-), group 4 (r--), others 4 (r--). Nobody can execute, owner can write, the world can read.
If you'd rather not memorize the table, the linux permission calculator flips between octal and symbolic both ways in real time.
755 vs 644: When to Use Each
755 (rwxr-xr-x) belongs on two things:
- Directories. You need the execute bit on a directory to
cdinto it. Without it, evenlson a parent fails on names inside. This is the single most surprising fact about Unix permissions for newcomers — execute on a directory means "enter," not "run." - Executable files. Shell scripts, compiled binaries, anything you actually run.
644 (rw-r--r--) belongs on everything else — config files, HTML, images, CSS, source code that gets read by an interpreter (like .py files imported as modules, or .js files served by a web server). You own it and can edit, others can read.
A common drive-by recipe to fix a freshly-uploaded directory:
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;
chmod +x scripts/*.sh
That sets sane defaults across the tree, then re-marks the actual scripts as executable. The full reference for find, xargs, and the rest of the toolchain I lean on lives in the bash cheatsheet.
Symbolic Mode: Sometimes Friendlier
Octal is great when you know the exact end state. Symbolic mode is great when you want a relative change:
chmod +x deploy.sh— add execute for everyone.chmod u+w,go-w report.txt— give the owner write, take it away from group and others.chmod -R a-w archive/— recursively strip write from everybody.
I use symbolic mode in shell history when I'm not yet sure what the file currently is. I use octal in scripts where I want to assert the exact final state regardless of what was there.
The Security Mistakes That Matter
A few categories of mistake show up repeatedly in postmortems:
777 on a web directory. This was the cause of my own compromise. World-writable means anyone with shell access — or a web process that gets tricked into writing a file — can drop a PHP backdoor in your docroot. Never use 777. If something "only works at 777," the bug is elsewhere (usually owner mismatch).
666 on a config file with credentials. World-readable database credentials are a real category of bug. .env files should be 600 (rw owner only) or 640 (owner + group). Same applies to SSH private keys, which ssh will outright refuse to use if they're group- or world-readable.
Removing execute from /home. If you chmod 600 /home/yourname, your web server (or any cron job running as you) can no longer reach files inside, because it lost the directory-entry bit. Permissions on parent directories cascade in surprising ways.
The setuid Trap
There's a fourth permission bit ahead of the three trios — the "special" bit — written as a leading digit: 4755, 2755, 1755. The 4 means setuid: when you execute the file, the kernel runs it with the file owner's privileges, not yours.
This is how passwd works. The file is owned by root and is setuid; when a normal user runs it, it briefly becomes root to write /etc/shadow, then drops back down.
It is also how a careless script becomes a privilege-escalation vulnerability. The rule I follow: never setuid a script. Setuid on a shell script is ignored on modern Linux (for good reasons), and on the older systems where it works, it's a known exploit vector. Setuid on a compiled binary you wrote is sometimes the right answer, but only if you have audited every input.
If you see -rwsr-xr-x (note the s instead of x in the owner position) on a file you didn't expect, ask why before you ignore it.
A Quick Reference
| Permission | Octal | What it's for | |------------|-------|---------------| | rwxr-xr-x | 755 | Directories, executables | | rw-r--r-- | 644 | Regular files (configs, docs, images, CSS, HTML) | | rw------- | 600 | Secrets (.env, SSH private keys, credentials) | | rwxr-x--- | 750 | Directories where group can read, others can't | | rw-rw-r-- | 664 | Shared editable files inside a team group | | --------- | 000 | "Don't touch this" sentinel |
If you find yourself reaching for vim to inspect a script's behavior before running it, the patterns are documented in the vim cheatsheet. And for the surrounding web-server config that's usually the next thing to debug after a permissions issue, the nginx cheatsheet has the directives I check first.
TL;DR
755 for things you enter or run. 644 for things you read. 600 for secrets. Never 777. Setuid only on audited binaries, never on scripts. That covers about 95% of the chmod calls you'll ever make.
Related tools
Made by Toolora · Updated 2026-05-26