Linux Permission Codes (Numeric / Octal)
Linux permission codes like 644 and 755 are an octal shorthand for the read/write/execute bits on a file or directory.
On a WordPress VPS, understanding them prevents the two most common mistakes: breaking the site with overly strict permissions, or "fixing" problems with unsafe 777.
- Permissions are three digits: owner/group/others.
- Each digit is a sum of
r=4,w=2,x=1. - Common baseline: files
644, directories755. - Directories need
xto be accessible (traversal). - Prefer targeted fixes; avoid blanket
chmod -Rwithout a plan.
The mental model
Permissions are evaluated against three user classes:
- owner (user)
- group
- others
Each class gets a sum of:
- read (
r) = 4 - write (
w) = 2 - execute (
x) = 1
So one digit describes one class. Three digits describe owner/group/others.
XYZ
X = owner
Y = group
Z = others
The 4-2-1 math (rwx -> octal)
rwx = 4 + 2 + 1 = 7
rw- = 4 + 2 + 0 = 6
r-x = 4 + 0 + 1 = 5
r-- = 4 + 0 + 0 = 4
--- = 0
Read permissions with ls -l
ls -l shows permissions in the symbolic form:
-rw-r--r-- 1 siteuser www-data 4180 Mar 1 12:00 wp-config.php
drwxr-xr-x 5 siteuser www-data 4096 Mar 1 12:00 wp-content
Breakdown:
- first char: file type (
-file,ddirectory) - next 3: owner permissions
- next 3: group permissions
- next 3: others permissions
Files vs directories (why x matters)
The x bit means different things:
- on files: execute (run it as a program)
- on directories: enter/traverse the directory
This is why a directory often needs x even if you are not "executing" anything.
Example:
chmod 644 wp-contentis usually wrong (it removes directory execute and breaks traversal)chmod 755 wp-contentis a common baseline
Common permission codes and what they mean
| Code | Symbolic | Typical meaning | Common use on a WordPress VPS |
|---|---|---|---|
600 | rw------- | owner read/write only | secrets: private keys, restrictive configs |
640 | rw-r----- | owner read/write, group read | wp-config.php in group-based models |
644 | rw-r--r-- | owner read/write, everyone read | most files (PHP, CSS, images) |
700 | rwx------ | owner full, nobody else | private directories |
750 | rwxr-x--- | owner full, group enter/read | shared dirs (owner + group) |
755 | rwxr-xr-x | owner full, everyone enter/read | most directories |
Avoid 777.
It grants world-write and usually indicates an ownership/group problem.
Fix identity and group strategy instead.
Converting quickly (examples)
Example: 755
- owner
7=rwx - group
5=r-x - others
5=r-x
This is a common directory baseline.
Example: 644
- owner
6=rw- - group
4=r-- - others
4=r--
This is a common file baseline.
Worked examples (common modes)
Use this section when you want to sanity check a mode quickly.
| Mode | Symbolic | Typical use |
|---|---|---|
600 | rw------- | secrets and restrictive configs |
640 | rw-r----- | secret file readable by a trusted group |
644 | rw-r--r-- | most WordPress files |
664 | rw-rw-r-- | group-collaboration file (use sparingly) |
700 | rwx------ | private directory |
750 | rwxr-x--- | shared directory (owner + group) |
755 | rwxr-xr-x | most directories |
775 | rwxrwxr-x | group-writable directory (uploads/cache in group model) |
Decode 640
- owner
6=rw- - group
4=r-- - others
0=---
This is useful when a file contains secrets but you want a trusted group to read it.
Decode 775
- owner
7=rwx - group
7=rwx - others
5=r-x
This is commonly used for group-writable directories in a shared group model.
Do not apply 775 everywhere.
Only use group write on directories that must be writable (uploads, caches, runtime dirs).
Numeric vs symbolic chmod
Numeric chmod sets all three classes at once (owner/group/others). Symbolic chmod modifies specific bits and is often safer for incremental changes.
Examples:
# Numeric: set exact mode
chmod 755 wp-content
chmod 644 wp-config.php
# Symbolic: add or remove bits relative to current
chmod g+w wp-content/uploads
chmod o-w wp-content/uploads
chmod u+x /usr/local/bin/deploy.sh
umask and default permissions
Default permissions on new files and directories are influenced by umask.
Typical default behavior on many servers:
- file created with base
666becomes644(umask022) - directory created with base
777becomes755(umask022)
Check your umask:
umask
Lab to see it in action:
rm -rf /tmp/umask-lab
mkdir -p /tmp/umask-lab
cd /tmp/umask-lab
touch file.txt
mkdir dir
stat -c '%A %a %n' file.txt dir
WordPress path guide (practical)
This table is a starting point for a typical Linux WordPress install. Adjust to your stack and ownership model.
| Path | Typical mode | Notes |
|---|---|---|
| WordPress directories | 755 | default directory baseline |
| WordPress files | 644 | default file baseline |
wp-content/uploads/ | 755 or 775 | 775 if group model and web user needs write |
| cache directories | 755 or 775 | only if the cache plugin needs write |
wp-config.php | 600 or 640 | secrets file; keep tight |
.htaccess (Apache/OLS) | 644 | may be updated by WordPress depending on settings |
Some plugins attempt to write to places they should not. Prefer correcting the plugin or choosing a different plugin over widening permissions globally.
Safer bulk changes (dry-run first)
Before running a bulk chmod on production, list what will be affected.
cd /var/www/html
find . -maxdepth 2 -type d -print | sed -n '1,50p'
Then apply the smallest change that fixes the problem.
WordPress practical defaults
These are patterns, not laws. Your stack (Nginx/Apache/OLS), deploy model, and PHP handler matter.
Common baseline:
- directories:
755 - files:
644 wp-config.php: tighter (commonly600or640)
Why wp-config.php is special:
- it contains database credentials and secrets
- it should not be writable by the world
- it should not be readable by unintended users
Apply permissions safely
Single path examples
chmod 755 wp-content
chmod 644 wp-config.php
# Show what you changed
stat -c '%A %a %U:%G %n' wp-content wp-config.php
Normalize a WordPress tree (directories vs files)
If you need to normalize a tree, do it in a controlled way:
- set directories to
755 - set files to
644 - tighten
wp-config.php
cd /var/www/html
# Directories
find . -type d -print0 | xargs -0 chmod 755
# Files
find . -type f -print0 | xargs -0 chmod 644
# Tighten secrets
chmod 600 wp-config.php
find + chmod can break a production site if you run it in the wrong directory.
Always confirm the path first (pwd, ls, realpath) and consider testing on staging.
Ownership vs permissions (do not skip this)
Many "permission" problems are actually ownership problems.
If your deploy user is siteuser and your web server runs as www-data, decide a model:
- strict model: only specific directories are writable by the web user
- group model: use
siteuser:www-dataand group write where needed
Use id to confirm group membership:
id -nG siteuser
id -nG www-data || true
Verify with stat and namei
stat is more precise than ls -l when you are debugging.
stat -c '%A %a %U:%G %n' wp-config.php
stat -c '%A %a %U:%G %n' wp-content/uploads
Example output:
-rw------- 600 siteuser:www-data wp-config.php
drwxr-xr-x 755 siteuser:www-data wp-content/uploads
If access fails, verify every parent directory is traversable:
namei -l wp-content/uploads
Runbook: fix a WordPress "uploads not writable" incident
This runbook assumes you already confirmed disk space and inode space are OK.
- Identify the web/PHP user.
- Check ownership and perms on
wp-content/uploads. - Fix ownership/group model.
- Verify by creating a test file.
cd /var/www/html
ls -ld wp-content/uploads
stat -c '%A %a %U:%G %n' wp-content/uploads
sudo -u www-data bash -lc 'touch wp-content/uploads/.perm_test && rm -f wp-content/uploads/.perm_test' || true
Do not "fix" this with chmod -R 777.
If the web user cannot write, align ownership and group access instead.
Troubleshooting matrix
| Symptom | Likely cause | Check | Safer fix |
|---|---|---|---|
| Uploads fail | wrong owner/group on uploads | ls -ld wp-content/uploads | set correct owner/group; ensure directory has x |
| Site shows 403 | missing directory traverse | namei -l /var/www/html | restore x on each directory |
| PHP files download instead of execute | wrong server/PHP handler | check server config/logs | fix handler; perms are not the real issue |
| SSH deploy works, browser fails | different user contexts | id; sudo -u www-data id | group model or targeted writable dirs |
Common mistakes
Mistake: making directories 644
Symptom: "Permission denied" when WordPress tries to access files inside.
Fix: directories need x.
Mistake: using 777 to "fix" uploads
Symptom: uploads work, but you introduced a security risk. Fix: correct ownership and group membership.
Mistake: recursive chmod on the wrong path
Symptom: permissions on system dirs or home dirs break unrelated services. Fix: use explicit paths and a restore plan.
Lab: decode and apply
- Decode these codes into symbolic form:
600640644750755
- Create a test folder and apply them:
mkdir -p /tmp/perm-lab/dir
echo hello >/tmp/perm-lab/file.txt
chmod 755 /tmp/perm-lab/dir
chmod 644 /tmp/perm-lab/file.txt
ls -ld /tmp/perm-lab/dir
ls -l /tmp/perm-lab/file.txt
- Observe what changes when you remove
xfrom a directory:
chmod 644 /tmp/perm-lab/dir
ls -ld /tmp/perm-lab/dir
# This will fail because you cannot traverse the directory.
ls -l /tmp/perm-lab/dir || true
Reference: octal lookup
0-7 lookup table
0 ---
1 --x
2 -w-
3 -wx
4 r--
5 r-x
6 rw-
7 rwx
Appendix: special bits (4-digit modes)
Some modes have a leading digit for special bits (setuid, setgid, sticky). These are not required for basic WordPress operations, but you will see them on system directories.
4xxx = setuid
2xxx = setgid
1xxx = sticky bit
If you need these, treat them as a separate topic from basic 644/755.