10 Common chmod Mistakes (and Why 777 Is Never the Answer)
chmod mistakes that break sites and open holes — chmod -R gone wrong, 777 panic-fixes, umask confusion, lost execute bits, and what to run instead.
chmod is fifty years old, fits in a tweet, and still breaks production every day — usually not because the command is hard, but because the model underneath is slightly different from what people assume. These are the mistakes that come up most, roughly in order of how much damage they cause.
1. chmod 777 as a debugging step
“Permission denied” → chmod 777 → it works → move on. This is the most common security mistake on shared systems, and it keeps working just enough to reinforce the habit.
777 grants read, write and execute to every account on the machine: other users, the web server, background daemons, and — on shared hosting — every other customer’s processes. A 777 PHP file is not just readable, it’s rewritable and runnable by anything: defacement and backdoor installation are the classic outcomes. Search any list of WordPress-hardening checklists and “no 777 files” is always on it, because attackers scan for them deliberately.
Run instead: 644 for files, 755 for directories. Then fix the actual problem, which is almost always one of:
- wrong owner —
chown www-data: fileorchown -R deploy:deploy app/ - the reader is in the wrong group —
chgrp, or add the user withusermod -aG - a directory missing
x(search) somewhere in the path — see #4
If 777 “fixed” it, the failure was access control working correctly. Find out who needed what.
2. chmod -R flattening files and directories together
chmod -R 755 dir/ does exactly what it says — which is the problem: every file under dir/ becomes executable (755), and every directory… fine, but the files are now wrong. Executable source files and images leak through ls, git diff --summary lights up with mode change 100644 => 100755, and on some setups executable user-uploaded files are a policy violation.
Run instead — split files from directories, or use conditional X:
find dir/ -type d -exec chmod 755 {} +
find dir/ -type f -exec chmod 644 {} +
# or, same effect in one pass:
chmod -R u+rwX,go+rX,go-w dir/
Capital X sets execute only on directories and files that already have it — built precisely for recursive jobs.
3. Assuming a bare clause means “everyone”
chmod +w file does not add write for group and others on a typical system. When the who letter is omitted, chmod filters the change through your umask — and umask 022 masks exactly the group/other write bits. Result: owner-only write, which is not what the command looks like it says.
chmod +w file # umask 022 → owner only gets +w
chmod a+w file # explicit 'a' → everyone gets +w (rarely what you want)
chmod go+w file # the usual intent: group and others
Same rule for = and -: chmod =rw on a 777 file yields 644, not 666. The calculator’s clause box applies the same umask rule (022) so its answers match your shell — check yours with umask.
4. Forgetting that paths are chains
Reaching /srv/app/public/logo.png requires execute on every directory in the path: /srv, /srv/app, /srv/app/public — plus read on the file. A perfectly-permissioned file inside a 700 home directory is unreachable for everyone else, full stop. This is the cause of most “the file is 644 but nginx still says forbidden” mysteries.
Diagnose the whole chain in one shot:
namei -l /srv/app/public/logo.png
Run instead: give the minimum traversal — chmod o+x on each directory (x alone allows reaching named files without letting anyone list them), or move the content out of the private directory entirely.
5. Confusing file permissions with directory operations
chmod 400 secrets.txt feels safe — “read-only!” — but rm secrets.txt still works (after a confirmation prompt), because deletion is a directory write operation. Same for rename and create. People discover this after scripting a cleanup that “couldn’t possibly delete read-only files.”
Corollary: chmod -w dir/ is how you actually freeze a directory’s contents — it blocks creating, deleting and renaming inside. And on shared dirs where everyone should write, the sticky bit (chmod +t) is what stops users deleting each other’s files — that’s the entire reason /tmp is 1777 and not 777.
6. setuid on a shell script (and other special-bit accidents)
chmod u+s myscript.sh looks like a neat way to let users run something privileged — and does nothing on Linux, where the kernel ignores setuid on interpreted scripts (a long-standing race-condition fix). Worse, on systems that don’t ignore it, setuid scripts are a canonical privilege-escalation hole.
The other direction bites too: an accidental chmod 6755 or fat-fingered +s leaves a real binary running as root for anyone who can invoke it. Audit occasionally: find / -perm -4000 -type f 2>/dev/null lists every setuid file; the list should be short and boring (passwd, sudo, su, mount…). Full mechanics in special bits explained.
7. Losing the execute bit on scripts (and over-granting it on data)
Two mirror-image slips: downloading or copying a script through a path that strips the mode (scp preserves it; extracting from a zip doesn’t; some git workflows flatten it) — then “Permission denied” on ./deploy.sh. The fix is chmod +x, but also check the shebang line survived: head -1 deploy.sh should show #!/bin/bash or similar as the first line.
The reverse: chmod +x on config files, data files or uploads marks them runnable for no reason. Harmless on the filesystem, but it signals sloppy hygiene — and if a noexec-vs-exec mount policy changes, world-executable user content is where trouble starts.
8. chown where chmod was meant (and vice versa)
“User can’t write to the directory” — two different fixes: make the directory writable by them (chmod g+w if they’re in the group, chmod o+w if everyone should — probably not), or make it owned by them (chown user: dir/). Reaching for chmod 777 because chown felt scarier is how #1 happens. Rule of thumb: who should own it → chown/chgrp; what each class may do → chmod. Most real permission problems are ownership problems wearing a chmod costume.
9. Octal typos that silently mean something else
chmod 0755 is fine (same as 755). chmod 755 file is fine. But chmod 7555 isn’t a typo error — it’s a valid four-digit mode: special bits 7 (setuid+setgid+sticky!) plus 555. Four digits where you meant three doesn’t just add a leading zero — the first digit is the special-bits digit. chmod 660 is a file nobody in the group can execute and others can’t touch — paste any suspect octal into the calculator to see the ls -l expansion before you run it.
10. “Fixing” SSH with relaxed permissions
SSH refuses to work when key files are too open — ~/.ssh must be 700, private keys 600, authorized_keys 600 (or the daemon ignores them with the cryptic “bad permissions” log line). People sometimes chmod keys more open to debug a login failure, which makes SSH reject them harder. The complete recipe:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa ~/.ssh/authorized_keys
chmod 644 ~/.ssh/id_rsa.pub ~/.ssh/known_hosts ~/.ssh/config
The two-minute audit
When permissions look wrong, in order:
ls -l— read the mode, owner, group. (Paste the string into the calculator if it’s still opaque.)namei -l /full/path— verify every directory above it allows search.id theuser— which class does the failing account actually fall into: owner? group member? other?- Fix the narrowest thing: the right class, the right bit, the right owner — never
777.
The last one is the whole article in one line.
Frequently asked questions
I ran chmod 777 to fix a permissions error — how bad is it?
Undo it now: chmod 644 for files, 755 for directories. 777 didn't fix the underlying problem — it removed the lock, and whatever was being denied access can now also modify and run the file. If the real issue was ownership, run chown instead; if it was a missing execute on a directory, add x to that one directory. Then find out what actually needed access.
chmod -R 755 made every file executable — how do I fix it?
Re-run with a split: find dir/ -type d -exec chmod 755 {} + and find dir/ -type f -exec chmod 644 {} +. Or preempt it next time with chmod -R a+rX dir/ — capital X sets execute only on directories and already-executable files.
Why did chmod +w only change the owner's write bit?
Because a clause with no class letter is filtered by your umask — with the typical 022, group/other write bits are masked out. This is the same rule real chmod follows. Write a+w or go+w to bypass the filter explicitly.
I can't cd into a directory I can clearly read — why?
Listing names needs read on the directory; entering and reaching files inside needs execute. chmod o+r dir without x lets people see filenames but not open them — you usually want o+rx. Also check every directory above it: any one missing x blocks the whole path (namei -l /full/path shows the chain).
Does chmod -w stop a file being deleted?
No. Deleting and renaming are directory operations — rm asks whether you may modify the directory, not the file. A read-only file in a writable directory is still deletable (rm will just ask for confirmation). To protect entries in a shared directory you need the sticky bit; to protect a file from your own accidents, chmod 400 at least makes you think twice.
Is chmod +x on a shell script enough to run it?
Only if it also has a valid shebang (#!/bin/bash as the literal first line) and the filesystem isn't mounted noexec (common on /tmp and some mounts). And the interpreter named in the shebang must itself be executable. Check with head -1 script.sh and findmnt -T ..