Skip to main content

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.

Quick Summary
  • Permissions are three digits: owner/group/others.
  • Each digit is a sum of r=4, w=2, x=1.
  • Common baseline: files 644, directories 755.
  • Directories need x to be accessible (traversal).
  • Prefer targeted fixes; avoid blanket chmod -R without 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.

permission-code-structure.txt
XYZ
X = owner
Y = group
Z = others

The 4-2-1 math (rwx -> octal)

rwx-to-octal-cheat.txt
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:

ls-l-symbolic-example.txt
-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, d directory)
  • 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-content is usually wrong (it removes directory execute and breaks traversal)
  • chmod 755 wp-content is a common baseline

Common permission codes and what they mean

CodeSymbolicTypical meaningCommon use on a WordPress VPS
600rw-------owner read/write onlysecrets: private keys, restrictive configs
640rw-r-----owner read/write, group readwp-config.php in group-based models
644rw-r--r--owner read/write, everyone readmost files (PHP, CSS, images)
700rwx------owner full, nobody elseprivate directories
750rwxr-x---owner full, group enter/readshared dirs (owner + group)
755rwxr-xr-xowner full, everyone enter/readmost directories
warning

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.

ModeSymbolicTypical use
600rw-------secrets and restrictive configs
640rw-r-----secret file readable by a trusted group
644rw-r--r--most WordPress files
664rw-rw-r--group-collaboration file (use sparingly)
700rwx------private directory
750rwxr-x---shared directory (owner + group)
755rwxr-xr-xmost directories
775rwxrwxr-xgroup-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.

warning

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:

chmod-numeric-vs-symbolic.sh
# 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 666 becomes 644 (umask 022)
  • directory created with base 777 becomes 755 (umask 022)

Check your umask:

check-umask.sh
umask

Lab to see it in action:

lab-umask-defaults.sh
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.

PathTypical modeNotes
WordPress directories755default directory baseline
WordPress files644default file baseline
wp-content/uploads/755 or 775775 if group model and web user needs write
cache directories755 or 775only if the cache plugin needs write
wp-config.php600 or 640secrets file; keep tight
.htaccess (Apache/OLS)644may be updated by WordPress depending on settings
warning

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.

dry-run-list-top-level-wp-tree.sh
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 (commonly 600 or 640)

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-examples.sh
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:

  1. set directories to 755
  2. set files to 644
  3. tighten wp-config.php
normalize-wp-perms-example.sh
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
warning

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-data and group write where needed

Use id to confirm group membership:

check-user-groups.sh
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-perms-owner-group.sh
stat -c '%A %a %U:%G %n' wp-config.php
stat -c '%A %a %U:%G %n' wp-content/uploads

Example output:

example-stat-output.txt
-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-check-path-permissions.sh
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.

  1. Identify the web/PHP user.
  2. Check ownership and perms on wp-content/uploads.
  3. Fix ownership/group model.
  4. Verify by creating a test file.
uploads-incident-checks.sh
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
warning

Do not "fix" this with chmod -R 777. If the web user cannot write, align ownership and group access instead.

Troubleshooting matrix

SymptomLikely causeCheckSafer fix
Uploads failwrong owner/group on uploadsls -ld wp-content/uploadsset correct owner/group; ensure directory has x
Site shows 403missing directory traversenamei -l /var/www/htmlrestore x on each directory
PHP files download instead of executewrong server/PHP handlercheck server config/logsfix handler; perms are not the real issue
SSH deploy works, browser failsdifferent user contextsid; sudo -u www-data idgroup 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

  1. Decode these codes into symbolic form:
  • 600
  • 640
  • 644
  • 750
  • 755
  1. Create a test folder and apply them:
lab-create-test-files.sh
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
  1. Observe what changes when you remove x from a directory:
lab-remove-directory-execute.sh
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
octal-lookup.txt
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.

special-bits-overview.txt
4xxx = setuid
2xxx = setgid
1xxx = sticky bit

If you need these, treat them as a separate topic from basic 644/755.