Setting WordPress file permissions defines which account can change the site tree and which account can only read it while PHP serves requests. A conservative pattern keeps routine uploads from becoming broad write access across core files, plugins, themes, and wp-config.php.
On a Linux host, ownership matters as much as the numeric mode. A common production split uses deploy as the account that owns and deploys the files, and www-data as the web server or PHP-FPM group that needs read access plus write access only where WordPress creates media files.
Use the web or PHP account for the actual stack, such as www-data on many Debian and Ubuntu hosts or apache on many RHEL-family hosts. Shared-hosting or suexec-style PHP may run as the site owner instead; in that model, keep files owned by the hosting account and avoid opening directories to 777.
$ cd /var/www/example.com/public_html
Use the directory that contains wp-config.php and wp-content for the live site. If the host keeps wp-config.php one directory above the public root, adjust the hardening step to that actual file path.
$ stat -c '%a %U:%G %n' . wp-config.php wp-content 755 deploy:www-data . 644 deploy:www-data wp-config.php 755 deploy:www-data wp-content
Unexpected owners such as root on files created by maintenance commands can break later uploads or updates. Fix the owning account before debugging WordPress itself.
$ sudo chown -R deploy:www-data /var/www/example.com/public_html
Replace deploy with the account used for SFTP, deployment, or shell maintenance. Replace www-data with the group used by the web server or PHP pool.
$ sudo find /var/www/example.com/public_html -type d -exec chmod 755 {} \;
755 lets the owner write and lets the web/PHP group traverse and read directories. Use 750 only when the web/PHP group has the required execute access through the full path.
$ sudo find /var/www/example.com/public_html -type f -exec chmod 644 {} \;
644 keeps core, plugin, and theme files writable by the owner while still readable by the web/PHP process.
$ sudo chmod 440 /var/www/example.com/public_html/wp-config.php
Use 400 when PHP runs as the same user that owns the file. Use a temporary writable mode only while editing wp-config.php, then return it to 440 or 400.
$ sudo install -d -o deploy -g www-data -m 2775 /var/www/example.com/public_html/wp-content/uploads
The leading 2 sets the setgid bit so new subdirectories inherit the www-data group.
$ sudo find /var/www/example.com/public_html/wp-content/uploads -type d -exec chmod 2775 {} \;
Apply the same directory pattern only to plugin-documented writable locations such as a cache directory. Do not make all of wp-content writable just to fix one plugin warning.
$ sudo find /var/www/example.com/public_html/wp-content/uploads -type f -exec chmod 664 {} \;
Existing media files may already be owned by the web/PHP account after browser uploads. The mode still needs to avoid world write access.
$ stat -c '%a %U:%G %n' wp-admin/index.php wp-config.php wp-content/uploads wp-content/uploads/2026/06/example.jpg 644 deploy:www-data wp-admin/index.php 440 deploy:www-data wp-config.php 2775 deploy:www-data wp-content/uploads 664 deploy:www-data wp-content/uploads/2026/06/example.jpg
$ sudo -u www-data touch wp-content/uploads/permission-check.tmp
Replace www-data with the PHP pool user when the process runs under a dedicated per-site account.
$ stat -c '%a %U:%G %n' wp-content/uploads/permission-check.tmp 644 www-data:www-data wp-content/uploads/permission-check.tmp
$ sudo rm wp-content/uploads/permission-check.tmp
$ find /var/www/example.com/public_html -perm -0002 -print
No output means the scan did not find any path writable by everyone. Fix any path printed by this command before handing the site back to normal traffic.
The new file should appear below wp-content/uploads while wp-admin, wp-includes, plugin files, theme files, and wp-config.php remain non-writable by everyone else. On SELinux-enforcing hosts, correct Unix modes may still fail until the upload tree has the appropriate HTTP write context.