Skip to content

How to Use chmod Recursively in Linux (Files vs Directories)

6 min read

Applies to: Linux servers and VPS; tested on Debian 13 (GNU coreutils 9.7), AlmaLinux 10.2 (GNU coreutils 9.5) and Ubuntu 26.04 (uutils coreutils 0.10.0)

To change the permissions of a folder and everything inside it, use chmod -R, for example chmod -R 755 /var/www/example.com. Be careful with it: -R gives files and folders the same mode, so chmod -R 755 makes every file executable. For a website or any normal folder tree, set directories and files separately instead:

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

This gives folders 755 (drwxr-xr-x) and files 644 (-rw-r--r--). The rest of this article shows what each method changes, how to check the result first, and how to repair a tree after chmod -R 777. If you are not sure what 755 or 644 mean, read what rwxr-xr-x (755) and drwxrwxr-x (775) mean first.

chmod on a folder vs chmod -R

Without -R, chmod changes only the folder itself, not its contents. With -R (or --recursive) it walks the whole tree. Lab example on Debian 13, starting from a small website folder:

$ chmod 700 site
$ find site -maxdepth 1 -printf '%M %m %p\n'
drwx------ 700 site
drwxr-xr-x 755 site/css
-rwxr-xr-x 755 site/deploy.sh
-rw-r--r-- 644 site/index.html
...

Only site changed. Now the recursive version:

$ chmod -R 755 site
$ find site -printf '%M %m %p\n'
drwxr-xr-x 755 site
drwxr-xr-x 755 site/css
-rwxr-xr-x 755 site/css/style.css
-rwxr-xr-x 755 site/index.html
-rwxr-xr-x 755 site/uploads/2026/photo.jpg
...

(output shortened). The folders are right, but index.html, the stylesheet and the photo are now executable, which they should not be.

Set directories to 755 and files to 644 (recommended)

  1. Check what is there before you change anything. This lists every file that is not 644 and every folder that is not 755:
    find /var/www/example.com -type f ! -perm 644 -printf '%m %p\n'
    find /var/www/example.com -type d ! -perm 755 -printf '%m %p\n'
    chmod has no dry-run option, so this is the way to preview.
  2. Set all directories to 755:
    find /var/www/example.com -type d -exec chmod 755 {} +
  3. Set all files to 644:
    find /var/www/example.com -type f -exec chmod 644 {} +
  4. Make scripts that must run executable again, one by one, for example chmod +x /var/www/example.com/deploy.sh. Step 3 removed their execute bit, as it did with deploy.sh in our test.

Result in the lab:

drwxr-xr-x 755 site
drwxr-xr-x 755 site/css
-rw-r--r-- 644 site/css/style.css
-rw-r--r-- 644 site/index.html
drwxr-xr-x 755 site/uploads
-rw-r--r-- 644 site/uploads/2026/photo.jpg

Use {} +, not {} \;. With +, find passes many files to one chmod call; with \; it starts chmod once per file. On 3,000 files in our Debian 13 container, -exec chmod 644 {} \; took 5.7 seconds and -exec chmod 600 {} + took under 0.1 seconds (lab example on a shared test machine, so only the difference matters). Both forms handle names with spaces correctly.

One command: chmod -R with a capital X

A capital X adds execute only to directories and to files that already have an execute bit. Combined with =, it can do the same job as the two find commands:

chmod -R u=rw,go=r,a+X /var/www/example.com

chmod applies the parts from left to right: u=rw,go=r first clears every execute bit, then a+X puts it back on directories only. On a tree that we had set to 777, this produced exactly 755 for folders and 644 for files on all three systems we tested, including Ubuntu 26.04, which uses the Rust coreutils version of chmod.

The shorter form you often see, chmod -R u=rwX,go=rX, does not clean up after 777: in our test every file kept its execute bit, because X keeps execute on files that already have it.

Why not chmod -R 777

777 lets every user and every process on the server change or replace the files. On a web server that includes any compromised script running under another account. It rarely fixes the real problem either: an upload folder that the web server cannot write to usually has the wrong owner, not too few permission bits. Fix the owner with chown and keep the modes at 755 and 644, or 775 and 664 for a folder a group shares.

To find world-writable files and folders after someone ran chmod -R 777:

$ find site -perm -o+w -printf '%m %p\n'
666 site/index.html
777 site/uploads

Then repair the tree with the two find commands above.

See what chmod changed

Add -c (--changes) to print only the files whose mode really changed, or -v to print every file. Lab example:

$ find site -type d -exec chmod -c 755 {} +
mode of 'site' changed from 0777 (rwxrwxrwx) to 0755 (rwxr-xr-x)
mode of 'site/css' changed from 0777 (rwxrwxrwx) to 0755 (rwxr-xr-x)
...
$ chmod -v 644 site/index.html
mode of 'site/index.html' retained as 0644 (rw-r--r--)

GNU chmod and the Rust version on Ubuntu 26.04 printed the same lines.

Common chmod examples

  • chmod +x backup.sh: make a script executable (644 became 755 in our test).
  • chmod o-r notes.txt: remove read access for other users only.
  • chmod -R g+w shared: let the group write everywhere in a shared folder (files 664, folders 775).
  • chmod -R o-rwx shared: shut out everyone who is not the owner or in the group (files 660, folders 770).
  • chmod --reference=ref.txt wp-config.php: copy the mode of one file to another.

Pitfalls and error messages

  • chmod -R 755 folder/* skips hidden files. The shell's * does not match names that start with a dot, so .htaccess or .env keep their old mode. In our test .dotfile stayed at 700. Name the folder itself: chmod -R 755 folder.
  • "chmod: changing permissions of 'file': Operation not permitted": only the owner of a file (or root) can change its mode. Run the command with sudo, or fix the owner first.
  • "Permission denied" right after chmod -R 644: as a normal user, chmod -R 644 removed the execute bit from our folders and the user could no longer open them. Repair with chmod -R u=rwX,go=rX folder, which restores execute on the folders.
  • "chmod: it is dangerous to operate recursively on '/'": you only see this with --preserve-root. Without that option chmod does run on / (--no-preserve-root is the default), so check the path twice before you press Enter, especially in scripts with variables.
  • Symbolic links: in our tests chmod -R did not follow a link inside the tree, so the file it pointed to kept its mode. A link you name directly, such as chmod 644 link, changes the file it points to.
  • setgid folders: chmod 755 on a folder with the setgid bit kept it (2775 became 2755). See setuid, setgid and the sticky bit if you use shared group folders.

Related articles: Linux permission codes 755 and 775 explained, how to change the owner of a file or folder (chown), how to read ls -l output, how to copy files with their permissions.

Related services

KVM VPS hosting

NVMe SSD and full root access, with Free Basic Managed Support.

Unmanaged dedicated servers

full root access and free IPMI KVM, with Free Basic Managed Support.

Linux server management

hardening, patching, monitoring and automation for Ubuntu, Debian, RHEL and AlmaLinux.

Was this answer helpful?

Still stuck? Open a ticket