You do not need root access to install a WordPress plugin from cPanel Terminal. If your hosting provider gives you Terminal or SSH access and WP-CLI is installed, you can check, install and activate a WordPress.org plugin from the command line.
The reusable Bash script in this guide works inside a normal cPanel account. It can process one WordPress site, several specified sites, or automatically scan the account for WordPress installations.
What The Script Does
- Checks whether the supplied directory contains a working WordPress installation.
- Skips the site when the selected plugin is already active.
- Activates the plugin when it is installed but inactive.
- Downloads, installs and activates the plugin when it is missing.
- Prints errors immediately in cPanel Terminal.
- Saves timestamped error logs inside your cPanel home directory.
- Prevents the script from processing directories outside your own cPanel account.
Requirements
- Access to cPanel Terminal or SSH.
- WP-CLI installed and available through the
wpcommand. - Permission to modify the WordPress files in your cPanel account.
- The WordPress.org slug of the plugin you want to install.
- A current backup before making changes across several websites.
Step 1: Open cPanel Terminal
Log in to cPanel and open Terminal, or connect to the cPanel account through SSH. Do not use sudo with the commands in this guide.
Confirm your cPanel username and home directory:
whoami
echo "$HOME"The home directory will normally look similar to:
/home/usernameStep 2: Confirm WP-CLI Is Available
Run the following command:
wp --infoIf WP-CLI is available, the command will display information about WP-CLI, PHP and the server environment.
For a WordPress site installed in public_html, confirm WP-CLI can detect it:
wp --path="$HOME/public_html" core versionStep 3: Find The WordPress Document Root
The primary domain is normally installed in $HOME/public_html. An addon domain may use a directory such as $HOME/example.com.
Search the cPanel account for WordPress configuration files:
find "$HOME" -maxdepth 3 -type f -name wp-config.php -printExample results:
/home/username/public_html/wp-config.php
/home/username/example.com/wp-config.php
The corresponding WordPress document roots are:
/home/username/public_html
/home/username/example.com
Step 4: Create The cPanel Plugin Installer
Copy the complete command below, paste it into cPanel Terminal and press Enter. It creates the installer in $HOME/wp-plugin-installer, applies safe permissions and checks the Bash syntax without changing WordPress.
Step 5: Find The WordPress.org Plugin Slug
set -e
mkdir -p "$HOME/wp-plugin-installer/logs"
chmod 700 "$HOME/wp-plugin-installer"
chmod 700 "$HOME/wp-plugin-installer/logs"
cat > "$HOME/wp-plugin-installer/install-wp-plugin.sh" <<'EOF'
#!/usr/bin/env bash
set -uo pipefail
umask 077
shopt -s nullglob
SCRIPT_DIR="$HOME/wp-plugin-installer"
LOG_DIR="$SCRIPT_DIR/logs"
usage() {
cat <<USAGE
Usage:
$0 <plugin-slug> [wordpress-path ...]
Examples:
$0 pubsubhubbub
$0 pubsubhubbub "$HOME/public_html"
$0 pubsubhubbub "$HOME/public_html" "$HOME/example.com"
With no WordPress path, the script scans public_html and immediate
domain-named directories below your cPanel home directory.
USAGE
}
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
exit 0
fi
if (( $# < 1 )); then
usage >&2
exit 2
fi
PLUGIN_SLUG="$1"
shift
if [[ ! "$PLUGIN_SLUG" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
printf '[FATAL] Invalid plugin slug: %s\n' "$PLUGIN_SLUG" >&2
printf 'Use only lowercase letters, numbers and hyphens.\n' >&2
exit 2
fi
mkdir -p "$LOG_DIR"
RUN_ID="$(date '+%Y%m%d-%H%M%S')"
ERROR_LOG="$LOG_DIR/${PLUGIN_SLUG}-errors-$RUN_ID.log"
LATEST_ERROR_LOG="$LOG_DIR/${PLUGIN_SLUG}-latest-errors.log"
: > "$ERROR_LOG"
ln -sfn "$(basename "$ERROR_LOG")" "$LATEST_ERROR_LOG"
WP_CLI_BIN="$(command -v wp 2>/dev/null || true)"
if [[ -z "$WP_CLI_BIN" ]]; then
printf '[FATAL] WP-CLI was not found in PATH.\n' | tee -a "$ERROR_LOG" >&2
exit 1
fi
HOME_REAL="$(readlink -f -- "$HOME" 2>/dev/null || true)"
if [[ -z "$HOME_REAL" || ! -d "$HOME_REAL" ]]; then
printf '[FATAL] Could not resolve your cPanel home directory.\n' | tee -a "$ERROR_LOG" >&2
exit 1
fi
auto_scan=0
if (( $# > 0 )); then
CANDIDATES=("$@")
else
auto_scan=1
CANDIDATES=("$HOME/public_html" "$HOME"/*
fi
declare -A SEEN=()
candidates=0
checked=0
already_active=0
activated=0
installed=0
errors=0
record_error() {
local site="$1"
local stage="$2"
local detail="${3:-No command output was returned.}"
((errors += 1))
printf '[ERROR] %s -- %s\n' "$site" "$stage" >&2
printf ' %s\n' "$detail" >&2
{
printf '[%s] SITE: %s\n' "$(date '+%F %T')" "$site"
printf 'PLUGIN: %s\n' "$PLUGIN_SLUG"
printf 'STAGE: %s\n' "$stage"
printf '%s\n\n' "$detail"
} >> "$ERROR_LOG"
}
run_wp() {
"$WP_CLI_BIN" \
--path="$CURRENT_SITE" \
--skip-plugins \
--skip-themes \
--no-color \
"$@"
}
printf 'cPanel WordPress plugin run started: %s\n' "$(date '+%F %T')"
printf 'cPanel user: %s\n' "$(id -un)"
printf 'Plugin slug: %s\n\n' "$PLUGIN_SLUG"
for requested_path in "${CANDIDATES[@]}"; do
if [[ ! -d "$requested_path" ]]; then
if (( auto_scan == 0 )); then
record_error "$requested_path" "Directory check" "Directory does not exist."
fi
continue
fi
candidate="$(readlink -f -- "$requested_path" 2>/dev/null || true)"
if [[ -z "$candidate" ]]; then
record_error "$requested_path" "Path check" "Could not resolve the directory path."
continue
fi
case "$candidate" in
"$HOME_REAL"/*) ;;
*)
record_error "$candidate" "Path safety check" "The directory is outside your cPanel home directory."
continue
;;
esac
if (( auto_scan == 1 )); then
name="${candidate##*/}"
if [[ "$name" != "public_html" && ! "$name" =~ ^[A-Za-z0-9][A-Za-z0-9.-]*\.[A-Za-z]{2,63}$ ]]; then
continue
fi
fi
if [[ -n "${SEEN[$candidate]+present}" ]]; then
continue
fi
SEEN["$candidate"]=1
((candidates += 1))
if [[ ! -f "$candidate/wp-config.php" ]]; then
record_error "$candidate" "WordPress detection" "wp-config.php was not found; directory was not processed."
continue
fi
CURRENT_SITE="$candidate"
((checked += 1))
if ! output="$(run_wp core is-installed 2>&1)"; then
record_error "$candidate" "WordPress validation" "${output:-wp core is-installed returned a non-zero status.}"
continue
fi
if run_wp plugin is-active "$PLUGIN_SLUG" >/dev/null 2>&1; then
((already_active += 1))
printf '[SKIP - ACTIVE] %s\n' "$candidate"
continue
fi
if run_wp plugin is-installed "$PLUGIN_SLUG" >/dev/null 2>&1; then
if output="$(run_wp plugin activate "$PLUGIN_SLUG" 2>&1)"; then
((activated += 1))
printf '[ACTIVATED] %s\n' "$candidate"
else
record_error "$candidate" "Plugin activation" "$output"
fi
else
if output="$(run_wp plugin install "$PLUGIN_SLUG" --activate 2>&1)"; then
((installed += 1))
printf '[INSTALLED + ACTIVATED] %s\n' "$candidate"
else
record_error "$candidate" "Plugin installation/activation" "$output"
fi
fi
done
printf '\n%s\n' '================ SUMMARY ================'
printf 'Plugin: %s\n' "$PLUGIN_SLUG"
printf 'Candidate document roots: %d\n' "$candidates"
printf 'WordPress sites checked: %d\n' "$checked"
printf 'Already active/skipped: %d\n' "$already_active"
printf 'Existing plugin activated: %d\n' "$activated"
printf 'Installed and activated: %d\n' "$installed"
printf 'Errors: %d\n' "$errors"
printf 'Error log: %s\n' "$ERROR_LOG"
printf 'Latest error log: %s\n' "$LATEST_ERROR_LOG"
((errors == 0)) || exit 1
exit 0
EOF
chmod 700 "$HOME/wp-plugin-installer/install-wp-plugin.sh"
bash -n "$HOME/wp-plugin-installer/install-wp-plugin.sh"
echo "The cPanel plugin installer was created successfully."The script accepts a WordPress.org plugin slug rather than the plugin’s displayed name. The slug is the final part of the plugin’s WordPress.org URL.
For example, the WebSub plugin URL is:
https://wordpress.org/plugins/pubsubhubbub/
Its plugin slug is therefore:
pubsubhubbub
This version accepts official WordPress.org plugin slugs containing lowercase letters, numbers and hyphens. It does not accept premium plugin ZIP files or arbitrary download URLs.
Install A Plugin On The Primary Domain
If WordPress is installed in public_html, supply the plugin slug followed by the document root. This example installs WebSub:
"$HOME/wp-plugin-installer/install-wp-plugin.sh" \
pubsubhubbub \
"$HOME/public_html"Install A Plugin On An Addon Domain
If the addon domain’s document root is $HOME/example.com, run:
"$HOME/wp-plugin-installer/install-wp-plugin.sh" \
pubsubhubbub \
"$HOME/example.com"Install A Plugin On Several Domains
You can supply several WordPress document roots in the same command:
"$HOME/wp-plugin-installer/install-wp-plugin.sh" \
pubsubhubbub \
"$HOME/public_html" \
"$HOME/example.com" \
"$HOME/anotherdomain.com"Only the specified WordPress installations will be processed.
Automatically Scan The cPanel Account
When no WordPress path is supplied, the script checks $HOME/public_html and immediate domain-named directories below $HOME:
"$HOME/wp-plugin-installer/install-wp-plugin.sh" pubsubhubbub
The automatic scan recognises layouts such as:
/home/username/public_html
/home/username/example.com
/home/username/anotherdomain.net
If an addon domain uses a nested or nonstandard document root, provide its full path explicitly instead of relying on the automatic scan.
Install A Different WordPress Plugin
Replace pubsubhubbub with the slug from the required plugin’s WordPress.org URL:
"$HOME/wp-plugin-installer/install-wp-plugin.sh" \
plugin-slug \
"$HOME/public_html"View The Help Screen
"$HOME/wp-plugin-installer/install-wp-plugin.sh" --helpUnderstanding The Status Messages
SKIP – ACTIVE
[SKIP - ACTIVE] /home/username/public_html
The selected plugin is already installed and active. No changes were made.
ACTIVATED
[ACTIVATED] /home/username/public_html
The plugin was already installed but inactive. The script activated it.
INSTALLED + ACTIVATED
[INSTALLED + ACTIVATED] /home/username/public_html
The plugin was missing, so the script downloaded, installed and activated it.
ERROR
[ERROR] /home/username/public_html -- Plugin activation
The WordPress site or plugin operation failed. The error appears in Terminal and is also written to the plugin’s error log. The script continues checking the remaining sites.
Review The Error Logs
All error logs are stored under:
$HOME/wp-plugin-installer/logs
Display the most recent WebSub error log with:
cat "$HOME/wp-plugin-installer/logs/pubsubhubbub-latest-errors.log"
List every saved log with:
ls -lh "$HOME/wp-plugin-installer/logs"
Can The Script Be Run Again?
Yes. The script is designed to be rerun. Websites where the selected plugin is already active are skipped automatically.
The script does not update a plugin that is already installed and active. Its purpose is to detect, install and activate the selected plugin while producing a clear record of any WordPress installations that could not be processed.
Using The Script Across Separate cPanel Accounts
A normal cPanel user can only modify files owned by that account. If your websites are spread across separate cPanel accounts, log in to each account individually, create the installer under that account’s home directory and run it from that account’s Terminal.
