"Unable to Locate WordPress Content Directory (wp-content)": WordPress Is Looking Through FTP
The error only happens when WordPress updates files over FTP or SSH instead of writing them directly. It then has to find wp-content as the FTP account sees it, and a chrooted account, a symlinked path or blocked passive ports make that search fail.
"Unable to locate WordPress content directory (wp-content)." means WordPress is updating files through FTP or SSH, not directly, and could not find wp-content as that connection sees the filesystem. The folder exists. The FTP account's view of it does not match the server path WordPress is looking for. Either tell WordPress the FTP path with FTP_CONTENT_DIR, or fix file ownership so WordPress writes directly and never has to search.
Where WordPress raises it
The string is defined in WP_Upgrader as fs_no_content_dir and returned at several points of an update:
| When | Function |
|---|---|
| Connecting to the filesystem before any plugin, theme or core update | WP_Upgrader::fs_connect() |
Unpacking the downloaded package into wp-content/upgrade/ |
WP_Upgrader::unpack_package() |
| Moving the old version aside before replacing it (WordPress 6.3+) | WP_Upgrader::move_to_temp_backup_dir() |
| Restoring or deleting that backup | restore_temp_backup(), delete_temp_backup() |
Outside updates, the same lookup appears in two places. Tools › Site Health has a critical test labelled "Unable to locate WordPress content directory" in its check of the upgrade-temp-backup folder. The scheduled cleanup of old backups writes the full sentence as a PHP notice, visible in the debug log when WP_DEBUG is on.
Why it can only happen over FTP or SSH
Every one of those checks calls $wp_filesystem->wp_content_dir(), which calls find_folder( WP_CONTENT_DIR ). What find_folder() does depends on the filesystem method:
directreturns the path it was given, with a trailing slash. No lookup, no failure possible.ftpext,ftpsockets,ssh2have to translate the server path, such as/home/acme/public_html/wp-content, into a path on the remote connection.
WordPress picks direct when it can create a test file and that file is owned by the same user as WordPress's own files. Otherwise it uses FTP or SSH and asks for credentials. FS_METHOD explains that choice in detail. So the first question is not "where is wp-content?" but "why is WordPress not writing directly?"
How the search works, and how it fails
For the FTP and SSH methods, find_folder() tries three things in order:
- Constants (FTP methods only). If
FTP_CONTENT_DIRis defined and the folder asked for isWP_CONTENT_DIR, that value is used as is. - The absolute path. If the server path exists on the connection too, it is used.
- A search from the login directory.
search_for_folder()lists the current directory, looks for each part of the server path in turn (home,acme,public_html,wp-content), follows any match, and as a last resort starts again from/.
The search usually works with an FTP account that is confined to its home folder. It fails in these situations:
- The account is limited to a subfolder. An FTP user created for one plugin or for an uploads folder cannot see
wp-contentat all. - The server path goes through a symlink. If
WP_CONTENT_DIRis/var/www/site/wp-contentbut/var/www/sitelinks to/srv/clients/42/site, the FTP account sees only the real names, and the parts of the path never match. - The listing is empty. WordPress puts FTP connections into passive mode, and directory listings travel over a separate data connection on a passive port. If a firewall blocks those ports, logging in succeeds and every listing comes back empty, so the search finds nothing.
- The account belongs to a different site. After a migration, saved FTP details can point to an account on another vhost.
The trap: a match in the wrong place
The last item has a worse variant than this error. When no part of the server path matches, the search falls back to any folder named wp-content in the directory it is looking at, starting with the login directory. If the server path is /var/www/live/wp-content and the FTP account's home is the root of a staging copy, the staging copy's wp-content qualifies. WordPress can quietly update the wrong copy, which shows up later as "the update succeeded but nothing changed", or as Destination folder already exists on a site you did not touch.
Defining the constants removes the guessing, and it is worth doing even when updates currently work.
Fix it
Preferred: make WordPress write directly. Give the WordPress files to the user PHP runs as. Once the test file and WordPress's files have the same owner, the method becomes direct, credentials stop being requested, and this error cannot occur. A shared group with group write permission is not enough on its own: the automatic choice compares owners, not groups, so that setup also needs FS_METHOD set to direct.
If you must use FTP: log in with the same FTP account in a client, note the paths it shows, and add them to wp-config.php:
define( 'FTP_BASE', '/public_html/' );
define( 'FTP_CONTENT_DIR', '/public_html/wp-content/' );
define( 'FTP_PLUGIN_DIR', '/public_html/wp-content/plugins/' );
These are FTP paths, as the client shows them after login, not server paths. WordPress adds a missing trailing slash itself. They apply only to the ftpext and ftpsockets methods.
If the method is ssh2: the FTP constants are ignored. Use an SSH account whose view of the filesystem includes the real WordPress path, or switch to direct writes.
If listings are empty: open the FTP server's passive port range in the firewall, or ask the host which range it uses.
Common mistakes
- Using server paths like
/home/acme/public_html/wp-content/inFTP_CONTENT_DIRon an account whose FTP root is/. - Defining the constants and switching the method to SSH, then wondering why nothing changed.
- Leaving credentials for an old account in
wp-config.phpafter a host change. - Treating it as a permissions problem on
wp-contentitself and setting it to 777. The folder is not unwritable; it was not found. Folder creation errors look different, see could not create directory.
Verify
- An FTP client logged in as the same account opens the path in
FTP_CONTENT_DIRand listspluginsandthemes. - A plugin update runs to the end; updating safely means trying a small plugin first.
- Tools › Site Health no longer lists "Unable to locate WordPress content directory".
- With direct writes, WordPress stops asking for FTP credentials altogether.
Frequently asked
- WordPress knows where wp-content is on the server's disk. The error means it could not find that folder through the FTP or SSH connection it uses for updates, where paths start from the account's home directory and can look completely different.
- No. WordPress applies the FTP_BASE, FTP_CONTENT_DIR, FTP_PLUGIN_DIR and FTP_LANG_DIR overrides only when the filesystem method name contains ftp, that is ftpext or ftpsockets. With ssh2 they are ignored, and WordPress relies on the real path or its own search.
- Yes. The Site Health test for the plugin and theme backup folder runs the same lookup with the credentials it can find without asking you, the ones saved in wp-config.php. The scheduled cleanup of old update backups uses the same credentials, so it fails too. Updates that succeed with other details typed at update time do not prove the saved ones work. Making updates use direct access removes both problems.