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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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.

  9. 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.

  10. 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
  11. 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.

  12. 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
  13. Remove the temporary upload write probe.
    $ sudo rm wp-content/uploads/permission-check.tmp
  14. 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.

  15. Upload a small test image from MediaAdd 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.