Home › Blog › Article

WordPress File Permissions: Why 777 Backfires

WordPress File Permissions: Why 777 Backfires

Set directories to 755 and files to 644, owned by the deployment user with the web server group as needed, never 777. Verify by running a read and execute test as the web user (for example sudo -u www-data test -x /var/www/html/wp-admin && echo ok). If the site loads, the admin saves settings, uploads work, and the command prints ok without any world-writable bits, permissions are correct. Anything else means you still have a mismatch between owner, group, or missing execute bits.

Symptoms and Fast Triage Commands

When WordPress file permissions are wrong the failure modes are specific. You see a blank page with HTTP 500, a “403 Forbidden” on /wp-admin/, update prompts asking for FTP credentials, or uploads failing with “Unable to create directory wp-content/uploads/2026/01”. Start with these commands to locate the problem fast.

# See owner, group, and mode of core paths
ls -ld /var/www/html /var/www/html/wp-content /var/www/html/wp-content/uploads
ls -l /var/www/html/wp-config.php /var/www/html/index.php

# Find anything world writable (the 777 danger zone)
find /var/www/html -type d -perm -0002 -print
find /var/www/html -type f -perm -0002 -print

# Show the effective web user (Debian/Ubuntu Apache or Nginx)
ps aux | egrep '(apache|httpd|nginx|php-fpm)' | grep -v grep

If find lists paths, you have 777 or world-writable entries. If the admin asks for FTP, the web user cannot write to wp-content even though it may look writable to you as root. If directories lack x, traversal fails and you get 403 before PHP even runs.

What 755 and 644 Mean

Unix modes are three triplets for owner, group, and others. 755 is rwxr-xr-x: the owner can read, write, and enter (execute) the directory; group and others can read and enter but not write. Directories need the execute bit to be traversed, so every WordPress directory must be 755 or stricter. 644 is rw-r--r--: the owner can read and write the file, everyone else can only read. PHP source such as index.php, wp-config.php, and theme files should be 644 so the interpreter can read them but no other system account can modify them. The difference between 644 and 777 is that 777 adds write for group and others, which lets any compromised process on the host alter your code. Use stat -c '%a %n' file to print the numeric mode, and remember that wp-config.php at 600 is even safer when your control panel supports it.

Owner vs Web User

Two accounts matter: the owner (often your SSH/deploy user or root) and the web user that PHP-FPM or the server runs as (commonly www-data, nginx, or apache). WordPress writes to wp-content/uploads, applies core updates, and writes cache files as the web user. If the owner is deploy:deploy and directories are 755, the web user can read and traverse but cannot create files inside uploads unless it owns that path or the group permits it. The fix is not 777; it is aligning ownership. Run chown -R deploy:www-data /var/www/html/wp-content and chmod -R 775 /var/www/html/wp-content/uploads so the group www-data can write while the owner keeps control. For the rest of the tree keep 755/644 with owner deploy:deploy. If you deploy over SFTP as the same user that runs PHP, ownership is unified and permissions stay simple. When you use a managed panel, check which pool user PHP 8.3+ runs as before deciding the group.

Get a Quote

Email + requirement + budget range — we reply with a quote within 24 hours.

Symptoms of a Missing Execute Bit

A directory without the execute bit cannot be entered, even if it is readable. If /var/www/html/wp-admin is 644 instead of 755, Apache returns 403 Forbidden and Nginx logs “permission denied” on open(). PHP may also fail to include files, producing “failed to open stream: Permission denied” in wp-content/debug.log when WP_DEBUG is true. A missing x on wp-content breaks the admin entirely; a missing x on wp-includes breaks front-end rendering. Test with sudo -u www-data ls /var/www/html/wp-admin; if you get “Permission denied” while root can list it, the execute bit or ownership is wrong. Restore it with chmod 755 on every directory and confirm no file needs x unless it is a CGI script, which WordPress core does not use.

Step-by-Step Repair Process

  1. Back up current state: run getfacl -R /var/www/html > perms-backup.txt or note owner and modes so you can roll back.
  2. Find the web user with ps aux | grep php-fpm and note the user and group.
  3. Remove world-writable bits: find /var/www/html -type d -exec chmod 755 {} + and find /var/www/html -type f -exec chmod 644 {} +.
  4. Set ownership for writable paths: chown -R deploy:www-data /var/www/html/wp-content where deploy is your user.
  5. Allow group write on uploads: chmod 775 /var/www/html/wp-content/uploads and any cache dir like wp-content/cache.
  6. Verify as the web user with the test commands in the next section and reload the site.

If the site breaks after this, restore from the backup: re-apply the exact chown lines you recorded or use setfacl --restore=perms-backup.txt. A common pitfall is running chmod -R 777 out of habit, which reintroduces the risk this guide exists to remove. Another pitfall is forgetting that some hosts use nginx not www-data; always confirm with ps first. Teams that standardize this during deployment can rely on our Custom WordPress Development workflow to bake correct modes into CI.

Verifying as the Web User

Verifying as the Web User

Running as root hides the real permission picture because root bypasses checks. To verify as the web user, use sudo -u to impersonate the account PHP runs under. This is the only reliable way to confirm the site can read config and write uploads.

# Confirm the web user can traverse and read core
sudo -u www-data test -r /var/www/html/wp-config.php && echo 'config readable'
sudo -u www-data test -x /var/www/html/wp-admin && echo 'admin traversable'

# Confirm the web user can write uploads
sudo -u www-data touch /var/www/html/wp-content/uploads/.perm-test && echo 'uploads writable'
sudo -u www-data rm /var/www/html/wp-content/uploads/.perm-test

# If PHP-FPM runs as another user, swap www-data for that name
sudo -u nginx test -x /var/www/html && echo 'nginx ok'

Each command should print its success line. If “config readable” fails, check group ownership and the 644 mode. If “uploads writable” fails, re-run the chown and chmod 775 steps. You can also drop a temporary PHP probe (<?php echo getmyuid(), ' ', get_current_user(); ?>) to print the runtime UID, but remove it immediately after testing. For teams deploying on Baota or LNMP stacks, our Deploy WordPress on Baota LNMP guide shows the same verification inside panel environments.

Why 777 Backfires

Setting 777 makes every file writable by every account on the machine. If a single unrelated service is compromised, the attacker can inject PHP into wp-content/plugins or rewrite index.php. Search engines and hosts may also flag the site, and some security plugins refuse to run. Because WordPress only needs write access to uploads and occasionally wp-content for updates, granting global write is unnecessary exposure. The correct model is least privilege: 755/644 everywhere, group-write only where the web user must create files. If you inherited a 777 site, repair it with the ordered process above and then monitor with find in a cron job. Keep a restore point using a tested method from our WordPress Site Backup and Restore reference so a permission mistake never becomes data loss.

Prevention and Lockdown

Put permission fixes into your deployment script so they run after every release. Add a weekly cron job: find /var/www/html -perm -0002 -print | mail -s 'World writable found' you@example.com. Use define('DISALLOW_FILE_EDIT', true); in wp-config.php to stop admin-based edits, and keep FS_METHOD unset so direct writes use the correct ownership. On Nginx with PHP 8.3 FPM, set listen.owner to match your deploy group in the pool config at /etc/php/8.3/fpm/pool.d/www.conf. Document the owner and group in your repo README so new staff do not guess. Review the official WordPress hardening documentation for the current 2026 baseline, and browse our technical column index for related server guides.

Action Checklist

  • Run the triage find and ls -ld commands to locate any 777 paths.
  • Reset directories to 755 and files to 644 across the whole install.
  • Align owner and web-user group on wp-content and uploads only.
  • Verify with sudo -u read, execute, and write tests as the PHP user.
  • Add a cron audit and bake the fix into your deployment pipeline.

Need expert help hardening your server and permissions? See our WordPress development services for audits, fixes, and ongoing care.

References & further reading

Need WordPress Development?

Free proposal and quote within 24 hours — we serve clients worldwide.

QQ