One of the most frequent operational challenges in Linux system administration occurs when setting filesystem permissions across nested folder hierarchies, such as web server roots (/var/www/), Git repositories, or network shares. Running a blunt recursive command like chmod -R 755 mistakenly grants execute permissions to plain text files, scripts, and media. Conversely, running chmod -R 644 strips execute bits from directories, which immediately prevents users and web servers (like Nginx and Apache) from traversing subfolders, resulting in 403 Forbidden errors. Linux administrators must apply different permissions to directories versus files.
The Standard Solution: Using find with -exec
The most precise, POSIX-compliant method for separating directory and file permissions is combining the find utility with chmod. By filtering with the -type flag, you target exactly one filesystem object type at a time.
1. Setting Permissions for Directories Only (755)
Directories require the execute bit (x) so users and processes can enter them (traverse) and list their contents. Standard web server and application directories typically require 755 (rwxr-xr-x):
find /path/to/target -type d -exec chmod 755 {} +
How this works:
-type d: Matches only directories, skipping regular files, symlinks, and sockets.-exec chmod 755 {} +: Executeschmod 755on all matched directories in large batches (using the terminating+instead of\;drastically improves performance by avoiding spawning a new chmod process for every individual folder).
2. Setting Permissions for Regular Files Only (644)
Standard documents, scripts, configuration files, and images should not have execute permissions unless they are compiled binaries or command-line scripts. Normal files should be set to 644 (rw-r--r--):
find /path/to/target -type f -exec chmod 644 {} +
How this works:
-type f: Matches only regular files, leaving directory execute bits completely untouched.-exec chmod 644 {} +: Applies read and write permissions to the file owner, and read-only access to group members and others.
The Fast Alternative: Symbolic chmod with Capital X
If you prefer a single command without invoking the find utility, GNU chmod supports a special symbolic mode: the capital X flag (conditional execute). The capital X instructs chmod to apply the execute permission only if the target is a directory, or if the file already had an execute permission set:
chmod -R u=rwX,go=rX /path/to/target
Here is how the symbolic permission translates:
u=rwX: Gives the owner read and write permissions, plus execute permission only on directories (or existing executables).go=rX: Gives group and others read permissions, plus execute permission only on directories.- This single command resets an entire directory tree so that all folders get
755and plain files get644, without modifying file execution states inadvertently.
Dry-Running Before Execution to Prevent Accidental Overwrites
When working on production servers, never run recursive permission commands blindly. You can preview which files or directories will be affected before applying changes:
find /var/www/html -type d -print | head -n 20
find /var/www/html -type f -print | head -n 20
Reviewing the first 20 entries confirms that your path and filters match the intended target hierarchy.
Handling Executable Scripts and Binaries
If your project directory contains legitimate shell scripts (e.g., .sh files) or compiled binaries that require execute permissions, running find -type f -exec chmod 644 {} + will strip their ability to run. To restore execution permissions to specific scripts after a global sweep:
find /path/to/target -type f -name "*.sh" -exec chmod +x {} +
This targeted command re-enables the execute bit exclusively on shell scripts while keeping HTML, PHP, CSS, and image files safely non-executable.