2026年11月12日

2026年11月12日

Fix WordPress Permission Errors: File Permissions and Admin Access

This article is a localized edition of the Japanese original. Read the Japanese original

WordPress "permission errors" come in two kinds: the server lacks write permission on files, or the user permission (role) data is broken. The first is fixed by restoring the right combination of owner and permissions, and you never need 777. This article shows how to tell the errors apart, confirm which user PHP runs as, and restore safe permissions.

First, identify the kind of error

The word "permission" covers problems in very different places. Use what you see on screen to narrow it down.

File permission problems (server side)

  • Uploading an image fails with an error saying the uploaded file could not be moved to wp-content/uploads/…
  • An error says the directory could not be created and asks whether its parent directory is writable by the server
  • Updating a plugin or theme shows a screen asking for FTP connection details
  • An update fails with "Could not create directory"
  • The whole page returns 403 Forbidden

User permission problems (database side)

  • You are logged in as an administrator but see "Sorry, you are not allowed to access this page."
  • Right after moving the site or changing the table prefix, almost no admin menus are shown

The first half of this article covers the former, the second half the latter.

Check which user runs PHP

File permission problems are usually caused by a mismatch between "the owner of the WordPress files" and "the user running PHP." Check these two first.

# User running PHP (for php-fpm, master runs as root and workers as the pool user)
ps -eo user,comm | grep -E 'php-fpm|apache2|httpd' | sort | uniq -c

# Owner and permissions of the WordPress files
ls -ld /var/www/html /var/www/html/wp-content /var/www/html/wp-content/uploads

With php-fpm, only the master process runs as root; the worker processes actually handle PHP. What matters is whether the worker user can write to the directory in question. As of October 2026, the package default is www-data on Debian/Ubuntu and apache on RHEL-based systems (often changed to nginx when used with Nginx). You can see each pool's user in the user = and group = lines of /etc/php/<version>/fpm/pool.d/*.conf on Debian/Ubuntu, or /etc/php-fpm.d/*.conf on RHEL-based systems.

The quickest way to know whether that user can write is to try it as that user.

sudo -u www-data touch /var/www/html/wp-content/uploads/perm-test.txt && echo OK
sudo rm -f /var/www/html/wp-content/uploads/perm-test.txt

# List permissions of every directory along the path
namei -l /var/www/html/wp-content/uploads

namei -l shows the owner and permissions of each directory along the path in order. Even if uploads itself is writable, a parent directory without execute permission (x) blocks access. This is easy to overlook.

Recommended permissions and how to think about ownership

The common baseline is "directories 755, files 644." These numbers alone do not decide whether PHP can write, though. That depends on who owns the files and which user PHP runs as. There are three typical setups.

Setup A: PHP runs as a dedicated site user

This is the setup of many shared hosting services and of VPSs with a separate PHP-FPM pool per site. The file owner and the PHP user are the same, so PHP can write with 755/644 as is. This is the easiest setup to manage.

Setup B: files owned by a deploy user, PHP runs as www-data

A deploy user (for example deploy) owns the code, and only the places that need writes, such as wp-content/uploads, are made writable for the www-data group (775/664). You can no longer update core or plugins from the admin screen, but the code is harder to modify even if a PHP vulnerability is exploited.

Setup C: www-data owns everything

All updates from the admin screen work, but if a PHP vulnerability is exploited, every file can be modified. It is convenient but the weakest setup defensively.

No setup needs 777. 777 is sometimes suggested as a stopgap when owners cannot be aligned, but it makes the files writable by other users and processes on the same server.

Commands to restore permissions in bulk

Record the current state before you start so you can roll back. The output of getfacl -R includes owners and groups as well as permissions, and setfacl --restore restores them.

WP_ROOT=/var/www/html
cd "$WP_ROOT"

# Save the current permissions (the redirect runs with your own permissions, so save to your home directory)
sudo getfacl -R . > ~/wp-perms.acl
# To restore, run in the same directory: cd /var/www/html && sudo setfacl --restore="$HOME/wp-perms.acl"

Example for setup C: make the PHP user the owner and set directories to 755 and files to 644 (for setup A, read www-data as the dedicated site user).

WP_ROOT=/var/www/html
sudo chown -R www-data:www-data "$WP_ROOT"

# Directories 755, files 644
sudo find "$WP_ROOT" -type d -exec chmod 755 {} +
sudo find "$WP_ROOT" -type f -exec chmod 644 {} +

# wp-config.php readable and writable by the owner only
sudo chmod 600 "$WP_ROOT/wp-config.php"

For setup B, make the deploy user the owner and grant group permissions only where writes are needed.

sudo chown -R deploy:www-data /var/www/html
sudo find /var/www/html -type d -exec chmod 755 {} +
sudo find /var/www/html -type f -exec chmod 644 {} +

# Only uploads is group-writable. setgid keeps the group of new files as www-data
sudo find /var/www/html/wp-content/uploads -type d -exec chmod 2775 {} +
sudo find /var/www/html/wp-content/uploads -type f -exec chmod 664 {} +

# In setup B, set wp-config.php to 640 so the group (PHP) can read it
sudo chmod 640 /var/www/html/wp-config.php

wp-config.php is where people often trip up. With 600 or 400, only the owner can read it. If the owner is deploy, PHP runs as www-data and you set 600, PHP cannot read the settings and the whole site stops displaying. Use 640 if the owner and the PHP user differ, and 600 or 400 if they are the same.

When updates ask for FTP details

Plugin updates ask for FTP connection details because WordPress compares "the owner of a file PHP just created" with "the owner of WordPress's own files" and decides they do not match. Writing directly in that case would leave files with mixed owners, so it asks to go through FTP instead.

The real fix is to align owners as in setup A or C. Once owners match, updates are written directly without any extra setting. Adding the following constant to wp-config.php forces direct writes, but if you use it while owners still differ, only the updated files change owner and cause other permission errors later.

// Only if detection still fails after aligning owners
define( 'FS_METHOD', 'direct' );

If you intentionally block writes as in setup B, do not update from the admin screen; run WP-CLI as the deploy user to update instead.

SELinux or disk space can also be the cause

If permissions and owners are correct but writes still fail, suspect restrictions at the OS level.

SELinux (RHEL, AlmaLinux, Rocky Linux, etc.)

With SELinux enabled, the web server can write only to directories labeled for writing. Check the label with ls -Z and give uploads the writable label.

ls -Zd /var/www/html/wp-content/uploads

# Register a persistent rule, then apply it
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/wp-content/uploads(/.*)?"
sudo restorecon -Rv /var/www/html/wp-content/uploads

chcon can change the label temporarily, but running restorecon reverts it, so registering with semanage fcontext is recommended.

Disk space and inodes

A full disk also causes write errors. Check both df -h and df -i. If inodes are exhausted, you cannot create files even when space remains.

When the admin screen says you are not allowed

If you are an administrator and still denied access, check the permission data in the database rather than files. WordPress stores role definitions in an option named {prefix}user_roles and each user's roles in user meta named {prefix}capabilities. If you changed the table prefix during a move but did not rename these keys, WordPress decides the user has no role.

Back up the database first, then check with WP-CLI.

wp db export before-role-fix.sql

# $table_prefix in wp-config.php
wp db prefix

# Roles assigned to the user
wp user get admin --field=roles

# Do the capabilities meta key names match the prefix?
wp db query "SELECT user_id, meta_key FROM $(wp db prefix)usermeta WHERE meta_key LIKE '%capabilities';"

If there is a mismatch, for example the meta key is wp_capabilities but the prefix is abc_, rename the meta keys to match the new prefix. Likewise, rename wp_user_roles in the options table to abc_user_roles.

# Example: old prefix wp_ -> new prefix abc_
wp db query "UPDATE abc_usermeta SET meta_key = REPLACE(meta_key, 'wp_', 'abc_') WHERE meta_key IN ('wp_capabilities', 'wp_user_level');"
wp db query "UPDATE abc_options SET option_name = 'abc_user_roles' WHERE option_name = 'wp_user_roles';"

If the role definitions themselves are broken, you can reset them to the defaults.

# Reset the administrator role's capabilities to the defaults
wp role reset administrator

# Reassign the administrator role to a specific user
wp user set-role admin administrator

If you edited roles with a role-management plugin, wp role reset also discards those changes. Save the output of wp role list and wp cap list administrator beforehand so you can compare later.

Confirm the fix

After the work, check in this order:

  • Upload one image from Media and check that thumbnails are generated
  • Update one plugin and check that you are not asked for FTP details (not applicable to setup B, where you do not update from the admin screen)
  • Under Tools → Site Health, on the Info tab, check that the filesystem permissions section shows no unwritable directories
  • Use ls -l to check that files created by the update have the same owner as the other files

The last check is often skipped, but if owner mismatches remain, the same error comes back on the next update.

Summary

  • Permission errors come in two kinds: file permissions and user role data. Tell them apart by the message shown
  • File permissions depend on the combination of the PHP user and the file owner. Test actual writes with sudo -u www-data touch
  • The baseline is directories 755 and files 644. Do not use 777; align owners, or grant group permissions only where writes are needed
  • wp-config.php: 600 if the owner and the PHP user are the same, 640 if they differ
  • FTP prompts are caused by owner mismatches. FS_METHOD is only a supplement after owners are aligned
  • If writes fail with correct permissions, check SELinux labels and disk space/inodes
  • For "not allowed" in the admin screen, suspect a mismatch between the table prefix and the meta key names first

Related articles:

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲