Install WordPress plugins without WP-admin access

Date posted: 2014-04-30
Last updated: 2026-10-04

Use the wp-content/install.php drop-in to install and activate WordPress plugins without wp-admin access, for example for unattended installations.

Install WordPress plugins without admin access, and automate your WordPress customization and plugin installation. WordPress has a little drop-in plugin option available in the form of /wp-content/install.php. This install.php file is not present by default, but when created it can be used to install plugins without wp-admin access. This might come in handy for unattended WordPress installations, customization, and so on.

Put an install.php file in wp-content/ with an array of plugin slugs: it looks up each plugin through the WordPress.org plugin API, downloads and unzips it into wp-content/plugins/, activates it and finally deletes itself. All you need is FTP access to the website.

I originally wrote this post in 2014. The plugins in the example (captcha and db-cache-reloaded-fix) are just examples, use your own plugin slugs. If you have shell access to the server, WP-CLI is the easier way today: wp plugin install <slug> --activate.

If you have access to the FTP environment of a WordPress website you can use this to force the installation of plugins, for example. Suppose you are a web hoster and you want your customers to install a captcha or cache plugin, this is what you can use. Of course this is very intrusive.

The install.php file requires the wp-admin/includes/plugin.php file, and then uses an array of plugin names to install. Files are downloaded automatically through WordPress's API, are unpacked and activated. All you need to do is populate the array with the plugin names you want to install.

WordPress install.php example code

Here you have a PHP example that installs a captcha and a db-cache plugin:

<?php
/**
 * Install WordPress plugins without WP-Admin access
 * Jan Reilink
 * Author URI: https://www.saotn.org
 */
require( "../wp-admin/includes/plugin.php" );
$wpkgr_selected_plugins = array ( 
  0 => 'captcha',
  1 => 'db-cache-reloaded-fix', 
);
if( $wpkgr_selected_plugins !== NULL ) {
  foreach ( $wpkgr_selected_plugins as $plugin ) {
    $request = new StdClass();
    $request->slug = stripslashes( $plugin );
    $post_data = array( 
      'action' => 'plugin_information', 
      'request' => serialize( $request )
    );
    $options = array( 
      CURLOPT_URL => 'https://api.wordpress.org/plugins/info/1.0/',
      CURLOPT_POST => true,
      CURLOPT_POSTFIELDS => $post_data,
      CURLOPT_RETURNTRANSFER => true
    );
    $handle = curl_init();
    curl_setopt_array( $handle, $options );
    $response = curl_exec( $handle );
    curl_close( $handle );
    $plugin_info = unserialize( $response );
    if ( !file_exists( WP_CONTENT_DIR . '/plugins/' . $plugin_info->slug ) ) {
      echo "Downloading and Extracting $plugin_info->name";
      $file = WP_CONTENT_DIR . '/plugins/' . basename( $plugin_info->download_link );
      $fp = fopen( $file, 'w' );
      /**
       * Don't turn off CURLOPT_SSL_VERIFYPEER, but fix your PHP configuration - find out how:
       * https://www.saotn.org/useful/dont-turn-off-curlopt_ssl_verifypeer-and-fix-your-php-configuration/
       */
      $ch = curl_init();
      curl_setopt( $ch, CURLOPT_USERAGENT, 'WPKGR' );
      curl_setopt( $ch, CURLOPT_URL, $plugin_info->download_link );
      curl_setopt( $ch, CURLOPT_FAILONERROR, TRUE );
      curl_setopt( $ch, CURLOPT_HEADER, 0 );
      @curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, TRUE );
      curl_setopt( $ch, CURLOPT_AUTOREFERER, TRUE );
      curl_setopt( $ch, CURLOPT_BINARYTRANSFER, TRUE );
      curl_setopt( $ch, CURLOPT_TIMEOUT, 120 );
      curl_setopt( $ch, CURLOPT_FILE, $fp );
      $b = curl_exec( $ch );
      if ( !$b ) {
        $message = 'Download error: '. curl_error( $ch ) .', please try again';
        curl_close( $ch );
        throw new Exception( $message );
      }
      fclose($fp);
      if ( !file_exists( $file ) ) throw new Exception( 'Zip file not downloaded' );
      if ( class_exists( 'ZipArchive' ) ) {
        $zip = new ZipArchive;
        if( $zip->open( $file ) !== TRUE ) throw new Exception( 'Unable to open Zip file' );
        $zip->extractTo( ABSPATH . 'wp-content/plugins/' );
        $zip->close();
      }
      else {
        die( "no zip possible" );
      }
      unlink( $file );
      echo "<strong>Done!</strong><br />";
    } //end if file exists
  } //end foreach
} //if plugins
echo "Configuring WordPress";
  function run_activate_plugin( $plugin ) {
    $current = get_option( 'active_plugins' );
    $plugin = plugin_basename( trim( $plugin ) );
    $current[] = $plugin;
    sort( $current );
    do_action( 'activate_plugin', trim( $plugin ) );
    update_option( 'active_plugins', $current );
    do_action( 'activate_' . trim( $plugin ) );
    do_action( 'activated_plugin', trim( $plugin) );
  }
if( $wpkgr_selected_plugins !== NULL ) {
  foreach ( $wpkgr_selected_plugins as $plugin ) {
    $request = new StdClass();
    $request->slug = stripslashes( $plugin );
    $post_data = array( 
      'action' => 'plugin_information', 
      'request' => serialize( $request )
    );
    $options = array( 
      CURLOPT_URL => 'https://api.wordpress.org/plugins/info/1.0/',
      CURLOPT_POST => true,
      CURLOPT_POSTFIELDS => $post_data,
      CURLOPT_RETURNTRANSFER => true
    );
    $handle = curl_init();
    curl_setopt_array( $handle, $options );
    $response = curl_exec( $handle );
    curl_close( $handle );
    $plugin_info = unserialize( $response );
    $daplugins = get_plugins( '/' . $plugin_info->slug );
    $paths = array_keys( $daplugins );
    $plugin_file = $plugin_info->slug . '/' . $paths[0];
    run_activate_plugin( $plugin_file );
  } //end foreach
} //if plugins
echo "<strong>Done!</strong>";
// Let's delete install.php
unlink( ABSPATH . 'wp-content/install.php' );
?>

The install.php file is automatically removed when it's finished.

The script downloads plugins over HTTPS with cURL. If that fails with a certificate error, don't turn off CURLOPT_SSL_VERIFYPEER, but fix your PHP configuration.

More on WordPress' install.php plugin drop-in and customizing WordPress installation

You can find more information in the following posts:

Installing plugins is one thing, keeping them safe is another: find vulnerable WordPress plugin versions fast using PowerShell.

I write these posts in my spare time, based on real problems from my day job as a sysadmin. If this one saved you some debugging time, a small donation is much appreciated. Thanks! 🙏

Leave a Comment