Setting Recursive Chmod Permissions

May 28, 20266 min read

The Recursive Permission Problem

Running chmod -R 755 /var/www seems straightforward — apply 755 to everything under /var/www. But this creates a common problem: 755 makes files executable, which is unnecessary and potentially dangerous for regular files. You usually want directories at 755 (rwxr-xr-x) and files at 644 (rw-r--r--).

A single chmod -R can't set different permissions for files versus directories. You need more targeted approaches.

The Standard Solution: Find + Chmod

The most widely used pattern separates files and directories:

# Directories: 755 (rwxr-xr-x)
find /var/www -type d -exec chmod 755 {} +

# Files: 644 (rw-r--r--)
find /var/www -type f -exec chmod 644 {} +

The {} + syntax batches matched paths into fewer chmod calls, making it significantly faster than {} \; for large directory trees.

Performance Comparison

Command Calls10,000 Files100,000 Files
{} \; (one per file)~10,000 chmod calls~100,000 chmod calls
{} + (batched)~100 chmod calls~1,000 chmod calls

Use {} + unless you have a specific reason for per-file execution.

Using Xargs for Large Trees

For very large directory trees, xargs can be faster than -exec:

find /var/www -type d -print0 | xargs -0 chmod 755
find /var/www -type f -print0 | xargs -0 chmod 644

The -print0 and -0 flags handle filenames with spaces or special characters safely. Never omit these flags when dealing with user-uploaded content or uncontrolled filenames.

The Chmod X Trick

The X (capital X) permission flag sets execute only on directories and files that already have execute permission:

chmod -R a+X /var/www

This is useful when you want to add execute to directories without making all files executable. However, it has a catch: files that already have any execute bit set will keep it. If your files are already 755 by mistake, a+X won't fix them — it preserves the existing execute bits.

Safe Pattern: Reset Then Apply

# First, strip all execute bits
chmod -R a-x /var/www

# Then add execute back for directories only
find /var/www -type d -exec chmod a+x {} +

Handling Special File Types

Executable Scripts

Scripts (.sh, .py, .js with shebangs) need execute permission:

# After setting 644 on all files, add execute back for scripts
find /var/www -type f -name "*.sh" -exec chmod 755 {} +

Web Server Document Roots

A secure web root typically follows this pattern:

PathPermissionsOwnerGroup
/var/www/755rootwww-data
/var/www/html/755www-datawww-data
Directories755www-datawww-data
Static files644www-datawww-data
Upload directories755www-datawww-data
Config files640rootwww-data

Shared Group Directories

For directories where a group needs write access:

# Set SGID bit so new files inherit the group
find /var/www/shared -type d -exec chmod 2775 {} +
find /var/www/shared -type f -exec chmod 664 {} +

The 2 (SGID) ensures new files are created with the directory's group rather than the creator's primary group.

Common Recursive Mistakes

1. Chmodding from Root

# DANGEROUS — breaks system binaries and configs
chmod -R 755 /

Always specify your target directory explicitly. Never run recursive chmod on / or /etc.

2. Forgetting Hidden Files

chmod -R processes hidden files, but find patterns like *.txt won't match .env. If you need to exclude hidden files:

find /var/www -type f -not -path '*/\.*' -exec chmod 644 {} +

chmod follows symlinks and modifies the target file. This can accidentally change permissions outside your intended directory tree. Use -P with find to avoid following symlinks:

find -P /var/www -type f -exec chmod 644 {} +

Dry Run First

Before running recursive chmod, preview what would change:

# List all files that would be modified
find /var/www -type f ! -perm 644 -print
find /var/www -type d ! -perm 755 -print

Only apply changes if the list looks correct.

Try It Yourself

Calculate chmod values visually and see exactly what each permission set allows. Our free tool converts between numeric, symbolic, and binary representations.

👉 Free Chmod Calculator