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.
Steps to set WordPress file permissions:
- Open the active WordPress document root.
$ 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.
- Review representative modes and ownership before changing them.
$ 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.
- Set the content owner and web/PHP group on the site tree.
$ 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.
- Set standard directory permissions across the site tree.
$ 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.
- Set standard file permissions across the site tree.
$ 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.
- Harden wp-config.php after the base file mode pass.
$ 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.
- Create the uploads directory with group write access.
$ 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.
- Set upload subdirectories to inherit the web/PHP 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.
- Set existing upload files to owner and group writable.
$ 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.
- Verify representative final modes.
$ 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
- Test that the web/PHP account can write to uploads.
$ 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.
- Confirm the upload write probe was created.
$ stat -c '%a %U:%G %n' wp-content/uploads/permission-check.tmp 644 www-data:www-data wp-content/uploads/permission-check.tmp
- Remove the temporary upload write probe.
$ sudo rm wp-content/uploads/permission-check.tmp
- Check for world-writable paths under the site tree.
$ 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.
- Upload a small test image from Media → Add New in wp-admin.
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.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.