Linux VPS

Set different permissions for files and folders recursively

Fix a whole folder tree in one go, giving directories 755 and files 644 with find, or using chmod's capital X, without making every file executable.

IntermediateLinux server5 min read

After uploading a website, unpacking an archive or copying files from Windows, permissions are often a mess: some files executable, some folders unreadable. A plain chmod -R cannot fix this, because files and folders need different values. This guide shows how to set folders and files separately across a whole tree, safely.

Before you start

  • A Linux VPS with Ubuntu 22.04 or 24.04, Debian 12, or AlmaLinux or Rocky Linux 8 or 9.
  • An SSH session with sudo, or as the owner of the files.
  • The exact path of the folder to fix, for example /var/www/example.com.
  • Ideally, a backup of the folder. See Back up a config before editing for quick copy habits.

Warning: These commands change every file under the path you give. A typo that points them at / or /etc can break the server and lock you out. Type the full path, check it, and never run them from a folder you have not confirmed with pwd.

Why one chmod -R is not enough

Folders need the execute bit so users and services can enter them. Regular files almost never need it. So:

  • chmod -R 644 site/ strips x from folders. Nothing below the top can be reached, and the site returns 403 errors.
  • chmod -R 755 site/ makes every file executable. That is untidy and, for uploads, a security risk.

The usual target is 755 for directories and 644 for files, with tighter values for secrets.

Method 1: find with -type

find can select only directories or only files, and run chmod on each set.

Set all directories to 755:

Bash
find /var/www/example.com -type d -exec chmod 755 {} +

Set all regular files to 644:

Bash
find /var/www/example.com -type f -exec chmod 644 {} +
  • -type d matches directories and -type f matches regular files.
  • -exec chmod... {} + passes many paths to each chmod call, which is much faster than one call per file.

Add sudo in front if the files belong to another user.

Dry run first

See what will be touched before changing anything. These commands only list:

Bash
find /var/www/example.com -type d | wc -l
find /var/www/example.com -type f ! -perm 644 | head

The second command shows files that do not already have 644, so you can see the scale of the change.

Method 2: chmod with capital X

The capital X adds execute only to directories, and to files that already have execute for someone. One command gives folders 755 and plain files 644:

Bash
chmod -R u=rwX,go=rX /var/www/example.com

It is fast and easy to remember. The catch is that a file that was wrongly executable stays executable. After an upload from Windows, where everything may arrive as 777, use Method 1, or strip x first:

Bash
chmod -R a-x,u=rwX,go=rX /var/www/example.com

a-x removes execute everywhere, then X adds it back to directories only.

Keep scripts executable

Some files need x, such as shell scripts or binaries in a bin folder. Restore them after the bulk change:

Bash
find /var/www/example.com/bin -type f -exec chmod 755 {} +
chmod 755 /var/www/example.com/deploy.sh

Tighten secrets

Config files with passwords should not be readable by everyone. Set them explicitly after the bulk change:

Bash
chmod 640 /var/www/example.com/.env
chmod 640 /var/www/example.com/wp-config.php

With 640, the owner can edit and the group, usually the web server group, can read. Make sure the group is right with chown. More detail for WordPress is in WordPress file permissions.

Skip parts of the tree

To leave a folder alone, such as .git or a cache, prune it:

Bash
find /var/www/example.com -path /var/www/example.com/.git -prune -o -type f -exec chmod 644 {} +

Everything except .git gets 644.

Fix ownership at the same time

Permissions only make sense with the right owner. A typical web root:

Bash
sudo chown -R deploy:www-data /var/www/example.com

Use nginx or apache in place of www-data on AlmaLinux and Rocky. The full picture is in chown: change file owner and group.

Check it works

Count anything that does not match the target values. Both commands should print 0:

Bash
find /var/www/example.com -type d ! -perm 755 | wc -l
find /var/www/example.com -type f ! -perm 644 | wc -l

Files you set on purpose, such as scripts or .env, will show up in the second count. That is expected. Finally, load the site in a browser and confirm pages and images load.

Troubleshooting

  • 403 Forbidden after the change: a folder lost its x bit, or a parent folder above the web root is too strict. Check the whole path with namei -l /var/www/example.com/index.html.
  • find says "missing argument to -exec": the command is missing {} + or \; at the end. Copy it again exactly.
  • "Operation not permitted" on some files: they belong to another user. Run the command with sudo.
  • A script stopped running: it lost x. Restore it with chmod 755 script.sh.
  • Uploads fail after the fix: the web server needs write access to the uploads folder. Grant it to the group with chmod 775 on that folder only.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.