WordPress asks for ftp credentials. What should I do?

WordPress asks for ftp credentials. What should I do?

Perhaps if you are reading this article you are facing the situation depicted on the picture above.

Usually I get such credential request from WordPress on the local developer’s machines and on servers that I setup manually using SSH. Don’t really remember if I’ve ever faced it on shared hosting accounts that are accessed via FTP.

You get that request trying to update WP or install/update plugins or themes.

No worries I’ll give you a quick solution (or two).

  • Well apparently you can try to provide credentials. Never did it, too paranoid for that :)
  • Cast a magic spell given below.

Magic Spell

  1. Put the following string to your wp-config.php:
    define('FS_METHOD', 'direct');
  2. Provide your http server with the write access to the whole WordPress folder, for example like that (update it with your own specific values):
    cd _wordpress_dir_
    chown -R _username_:www-data . # set the group to the one your httpd is using, www-data for nginx in my case
    chmod -R g+w . # add write access to the group
     

In fact sometimes you can perform only one of the steps (any one), but performing both gives you a 100% solution.

A little theory behind the spell

If you are curious enough you can dig /wp-admin/includes/file.php ~900 lines deep and find this function.

/**
 * Determines which method to use for reading, writing, modifying, or deleting
 * files on the filesystem.
 *
 * The priority of the transports are: Direct, SSH2, FTP PHP Extension, FTP Sockets
 * (Via Sockets class, or `fsockopen()`). Valid values for these are: 'direct', 'ssh2',
 * 'ftpext' or 'ftpsockets'.
 *
 * The return value can be overridden by defining the `FS_METHOD` constant in `wp-config.php`,
 * or filtering via {@see 'filesystem_method'}.
 *
 * @link https://codex.wordpress.org/Editing_wp-config.php#WordPress_Upgrade_Constants
 *
 * Plugins may define a custom transport handler, See WP_Filesystem().
 *
 * @since 2.5.0
 *
 * @global callable $_wp_filesystem_direct_method
 *
 * @param array  $args                         Optional. Connection details. Default empty array.
 * @param string $context                      Optional. Full path to the directory that is tested
 *                                             for being writable. Default false.
 * @param bool   $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable.
 *                                             Default false.
 * @return string The transport to use, see description for valid return values.
 */
function get_filesystem_method( $args = array(), $context = false, $allow_relaxed_file_ownership = false ) {
    $method = defined('FS_METHOD') ? FS_METHOD : false; // Please ensure that this is either 'direct', 'ssh2', 'ftpext' or 'ftpsockets'
 
    if ( ! $context ) {
        $context = WP_CONTENT_DIR;
    }
 
    // If the directory doesn't exist (wp-content/languages) then use the parent directory as we'll create it.
    if ( WP_LANG_DIR == $context && ! is_dir( $context ) ) {
        $context = dirname( $context );
    }
 
    $context = trailingslashit( $context );
 
    if ( ! $method ) {
 
        $temp_file_name = $context . 'temp-write-test-' . time();
        $temp_handle = @fopen($temp_file_name, 'w');
        if ( $temp_handle ) {
 
            // Attempt to determine the file owner of the WordPress files, and that of newly created files
            $wp_file_owner = $temp_file_owner = false;
            if ( function_exists('fileowner') ) {
                $wp_file_owner = @fileowner( __FILE__ );
                $temp_file_owner = @fileowner( $temp_file_name );
            }
 
            if ( $wp_file_owner !== false && $wp_file_owner === $temp_file_owner ) {
                // WordPress is creating files as the same owner as the WordPress files,
                // this means it's safe to modify & create new files via PHP.
                $method = 'direct';
                $GLOBALS['_wp_filesystem_direct_method'] = 'file_owner';
            } elseif ( $allow_relaxed_file_ownership ) {
                // The $context directory is writable, and $allow_relaxed_file_ownership is set, this means we can modify files
                // safely in this directory. This mode doesn't create new files, only alter existing ones.
                $method = 'direct';
                $GLOBALS['_wp_filesystem_direct_method'] = 'relaxed_ownership';
            }
 
            @fclose($temp_handle);
            @unlink($temp_file_name);
        }
    }
 
    if ( ! $method && isset($args['connection_type']) && 'ssh' == $args['connection_type'] && extension_loaded('ssh2') && function_exists('stream_get_contents') ) $method = 'ssh2';
    if ( ! $method && extension_loaded('ftp') ) $method = 'ftpext';
    if ( ! $method && ( extension_loaded('sockets') || function_exists('fsockopen') ) ) $method = 'ftpsockets'; //Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread
 
    /**
     * Filter the filesystem method to use.
     *
     * @since 2.6.0
     *
     * @param string $method  Filesystem method to return.
     * @param array  $args    An array of connection details for the method.
     * @param string $context Full path to the directory that is tested for being writable.
     * @param bool   $allow_relaxed_file_ownership Whether to allow Group/World writable.
     */
    return apply_filters( 'filesystem_method', $method, $args, $context, $allow_relaxed_file_ownership );
}
 

The thing is to determine if WP can modify itself it tries to create a temporary file. Than it compares temporary file owner with the owner of the WP script. If the temporary file is created and the owners do match WP doesn’t need any credentials. Otherwise WP tries to check whether filesystem can be accessed with ftp or ssh.

To shortcut this sophisticated checks you can set the FS_METHOD constant to “direct” value. And provide write access.

leonidas-upgraded-wp

{{"Show older comments..."|nls}}{{comments.length}} {{"of"|nls}} {{total}} {{"shown" | nls}}

{{'Comment'|nls}}:

{{(dialog.replies.length > 1?'Replies':'Reply')|nls}}: