The advice is everywhere, and it always sounds certain: run chmod 777 on the folder and the error goes away. It appears under every failed upload, every plugin that cannot save its settings, every update that dies with “destination folder not writable”. The worst part is that it works. The error really does go away.
What also happens: every account and every process on that server can now write to your files. On shared hosting that includes other customers’ sites and whatever a compromised script somewhere on the machine is running as. A world-writable directory is the easiest possible place to drop a PHP backdoor. 777 does not fix the problem, it removes the security that was catching it.
The frustrating truth is that the correct values for WordPress have been the same for about two decades: 755 for directories, 644 for files, something stricter for wp-config.php. When those values produce errors, the permissions are not wrong. The ownership is, and that is a different fix entirely.
This article walks through what the digits mean, why the standard set is what it is, and how to repair a tree that someone already ran 777 over. The calculator below does the octal arithmetic for you: tick the nine boxes, or type an octal or symbolic value, and it keeps all three representations in sync, flags dangerous combinations and hands you the exact chmod command. It runs entirely in your browser and nothing you type leaves the page.
File permissions calculator
Click a permission set together or type it as a number, watch the octal and symbolic spellings stay in sync, and get the right value for every WordPress file, with the reason behind it. Everything is worked out in this browser tab, nothing leaves your browser.
Three or four digits, each 0 to 7. The last valid value is still in use.
Nine characters in rwxr-xr-x style, s, S, t and T included. The last valid value is still in use.
Both spellings say the same thing: one digit or triad each for owner, group and others. A fourth digit in front carries the special bits, and a leading zero is swallowed, 0644 and 644 are the same value.
chmod 644 file.php
Run the find pair from inside the WordPress root directory and nowhere higher: started a level up, it walks through everything else your account owns and re-stamps that too.
Three Digits, Nine Bits
A permission value like 644 is three digits, and each digit is the sum of three bits: read is 4, write is 2, execute is 1. A 7 is all three (4+2+1), a 6 is read and write (4+2), a 5 is read and execute (4+1), a 4 is read only, a 0 is nothing. There are no other legal digits, which is why 778 is not a permission value and the calculator quietly refuses it.
The three digits address three audiences, in order: the owner of the file, the group of the file, and others, meaning every other account on the machine. That last class is bigger than it sounds. On a typical server, “others” includes the mail daemon, the backup agent, every cron job of every other customer, and whatever identity an attacker’s script happens to hold.
The bits also mean different things on a file than on a directory, and this is the detail most explanations skip.
The one that trips everyone up is x on a directory. It does not mean run, it means enter: without it you cannot change into the directory or reach anything inside it, even if you know the exact filename and the file itself is world-readable. That is the entire difference between 644 and 755. A directory at 644 is a locked room you are allowed to read the label on. Directories get the extra 1 in each digit precisely so the same audiences that may read the files can actually reach them.
The reverse detail matters too: w on a directory controls the name table, not the file contents. A user with write on the folder can delete or rename a file inside it even when the file itself is 444. Permissions on the containing directory matter as much as the file’s own bits, which is also why the sticky bit exists (more on that further down).
The Standard WordPress Set
The five presets in the calculator are the whole recommended layout. Directories 755: the owner reads, writes and enters, everyone else reads and enters. Files 644: the owner reads and writes, everyone else reads. Nobody except the owner ever needs write, and nothing needs execute on a file.
Why do group and others get read at all? Because on many stacks the web server that delivers static assets is a different user from the one that owns them. nginx or Apache running as www-data reads your CSS, images and JavaScript through the others digit. Take that 4 away and the assets start returning 403.
wp-config.php is the exception in the strict direction. It holds the database credentials and the authentication keys, and exactly one process needs to read it: PHP. On modern hosting PHP runs as the file owner, so 600 (owner read and write, nothing else) works and gives away nothing. If your host still runs PHP as a separate user, 600 will white-screen the site, and you fall back to 640 with the right group, or 644 as a last resort. Which constants in that file are worth protecting is a topic of its own: wp-config.php explained.
.htaccess stays at 644 because Apache reads it as the server user on every single request, so it must remain world-readable, and WordPress itself rewrites it when you change permalink settings, so the owner keeps write. If a security plugin locks it to 444, permalink changes silently stop reaching the file. What the lines inside it actually do is covered in the htaccess explainer.
The uploads tree is the one place WordPress must create files and directories at request time: any upload can mint a new year and month folder. It still only needs 755, because on a healthy setup the PHP process is the owner. If uploads die with “Unable to create directory wp-content/uploads/2026/08”, the missing piece is almost never a permission digit. Check who owns the uploads folder before touching chmod, and if the message is the generic one, the HTTP error causes are worth ruling out first.
Core agrees with these numbers in its own source. When WordPress writes files through its filesystem API (updates, plugin installs), it uses two constants, defined in wp-admin/includes/file.php if you have not set them yourself:
define( 'FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) );
define( 'FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) );
Read the OR: whatever your WordPress root already has, but at least 755 and 644. And when the upload handler saves a file, it copies the permissions of the folder the file lands in, minus all execute bits:
$stat = stat( dirname( $new_file ) );
$perms = $stat['mode'] & 0000666;
chmod( $new_file, $perms );
That & 0000666 mask is why uploaded images never carry execute bits. It is also why a 777 uploads folder quietly produces 666 files: world-writable, forever, on every future upload. Wrong permissions propagate themselves.
Ownership Decides Everything
Here is the question the 777 advice never asks: which user is PHP? chmod writes three answers, one per digit, but the kernel reads only one of them per access, and it picks by identity. If the process is the owner, the first digit answers. If it is in the file’s group, the second. Otherwise the third.
On most current hosting (PHP-FPM pools, suEXEC, LiteSpeed, virtually every managed WordPress host) PHP runs as the same account that owns the files. Updates, uploads and cache writes are all answered by the owner digit, which is 6 or 7 everywhere in the standard set. Group and others never need write. That is exactly why 755 and 644 are enough.
The setup where they are not enough is classic mod_php: PHP embedded in Apache, running as www-data, while the files belong to the account you deploy with. Now PHP is neither owner nor group member and gets the last digit: 4, read only. Every write fails, someone suggests 777, and it works because you just gave write access to literally everyone, which happens to include www-data. That is the whole 777 phenomenon: a correct diagnosis (PHP cannot write) with the most destructive available treatment.
The real fix is one of two moves. Either give the files to the user PHP runs as (chown -R, on a server you control), or move PHP so it runs as the owner (an FPM pool per site, or a request to your host, who has almost certainly already set it up that way). To find out which user PHP actually is, do not guess:
ps -o user= -C php-fpm # or: -C apache2, -C httpd
stat -c '%U %G' wp-config.php
If the first command’s user and the second command’s owner match, the owner digit is answering and the standard set just works. If they differ, that mismatch is your actual problem, and no chmod value fixes it.
Repairing a Tree After 777
Once someone has run chmod -R 777, or an installer has left random values behind, fix it wholesale. Two commands reset an entire installation to the standard set:
cd /var/www/example.com/htdocs # your WordPress root
find . -type d -exec chmod 755 {} +
find . -type f -exec chmod 644 {} +
chmod 600 wp-config.php
Three things about these lines. First, the cd matters more than anything else: find recurses through everything below the current directory, so run the pair only from inside the WordPress root, and confirm with pwd and ls wp-config.php that you are where you think you are. Pointed at /, it will happily re-permission the operating system. Second, the {} + ending batches thousands of paths into a few chmod invocations instead of one process per file, which on a large uploads library is the difference between two seconds and several minutes. Third, the order of the two find lines does not matter, because -type d and -type f never overlap.
The calculator prints this pair for you when you use the two directory presets: the -type d line follows whatever octal is currently set, the -type f line stays at 644, and the copy button takes both lines along.
The Execute Bit on PHP Files Does Nothing
A surprising number of tutorials recommend 755 on .php files. The x bit tells the kernel it may load the file as a program through execve. Web PHP never takes that path: mod_php reads the script inside the Apache process, and PHP-FPM receives the path as SCRIPT_FILENAME over FastCGI and opens it like any data file. Both need the read bit only. A PHP file at 644 executes exactly as well as one at 755.
The one place x on a PHP file is real is the command line: a script with a shebang, run directly.
#!/usr/bin/env php
<?php
// now ./script.php works, and this file genuinely needs +x
Inside a WordPress tree that case does not exist, which is why the calculator warns when it sees execute bits on what it takes to be a file. Execute on .php buys you nothing and makes a smuggled-in file marginally more useful to whoever put it there.
The Fourth Digit
Sometimes a value arrives with four digits: 4755, 2775, 1777. The leading digit holds the special bits: setuid (4), setgid (2) and sticky (1). Setuid and setgid on a file mean the program runs with the owner’s or the group’s identity instead of the caller’s. In a web tree that is somewhere between pointless and alarming, and the calculator flags it accordingly.
Two directory uses are legitimate. Setgid on a directory (2775) makes new files inherit the directory’s group instead of the creator’s primary group, which genuinely helps when an SFTP user and the PHP user share a group and both write to the same folder. Sticky on a directory (1777) means only a file’s owner may delete it; that is what keeps users from deleting each other’s files in /tmp. In symbolic notation the special bits ride on the x position of their triplet: s where the x underneath is also set, S where it is not, and t or T in the others triplet for sticky. A three-digit octal like 644 deliberately means “no special bits”, which is why typing one into the calculator clears them.
Strip away the octal and file permissions are one question asked three times: who are you? Owner, group member or stranger, one digit each. Every WordPress permission problem is therefore one of two things. Either the digits are wrong, which the standard set and two find commands repair in under a minute. Or the identity is wrong, which no chmod value can repair and which 777 merely surrenders to.
So the next time a forum thread offers the magic number, translate it: “we do not know which user PHP runs as, so we allow everyone”. You can do better with two commands. Ask ps who runs PHP, ask stat who owns the files. If they match, set 755 and 644 and stop. If they do not, fix the ownership. The digits were never the problem.