Skip to content
System nominalUTC --:--:--Articles 124

How to Upload a WordPress Plugin, and the Tools That Help

The dashboard upload takes four clicks. The ZIP has to be built correctly, and the errors are specific. Every method, and what to check afterwards.

Martin Kiel · @mkiel

WordPress internals

Topic
TOOL
Section
Tooling & Workflow
Published
20 OCT 2026
Reading time
7 MIN
Words
1800W
Technical cover graphic for How to Upload a WordPress Plugin, and the Tools That Help
Generated schematic — TOOL series, REV 2.6.

The plugin was a 14 MB ZIP from a vendor, downloaded to a laptop and uploaded through the dashboard. WordPress answered with "The link you followed has expired." The owner downloaded it again, tried another browser, and then a third time, before discovering that the host's post_max_size was 8 MB. The file was never broken. The upload never arrived, and WordPress reported the most misleading error it has.

Uploading a plugin is one of the few tasks where the interface hides the mechanism. There are four ways to do it, the dashboard is the easiest, and each of the other three exists because the dashboard failed for a specific reason. Knowing the shape of the ZIP and the meaning of the errors turns a frustrating half hour into a two-minute job.

This is the practical guide: the structure, the four tools, the errors, and the checks after activation.

Disclosure: some links on this page are affiliate links, and WPSifero may earn a commission if you buy through them. That does not change the price, and the upload process below is the same whether the plugin came from the directory, a marketplace or a developer.

The dashboard upload is four clicks, and it has limits

In the WordPress admin, go to Plugins, then Add New Plugin, then Upload Plugin. Choose the ZIP, click Install Now, then Activate. That is the whole workflow, and for a plugin under the host's upload limit it is the right one.

The limits are the interesting part. PHP has upload_max_filesize and post_max_size, and the effective limit is the smaller of the two, minus the overhead of the form. A host with an 8 MB post limit rejects a 14 MB ZIP before WordPress sees it, which is why the error mentions a link rather than a size. The same happens when the upload nonce expires on a slow connection, because the request that arrives has no valid form data.

You can raise the limits in php.ini, in the host's PHP settings, or in a .user.ini file if the host allows per-directory overrides. On a managed host, the settings may be fixed and the correct answer is one of the other methods. Raising the limit is worth doing once for a site that installs plugins regularly, because the failure is confusing every time.

Check the real limit before uploading

A quick phpinfo() page or the host's PHP settings screen shows both values. The smaller one is the ceiling, and it applies to the whole request, including the form fields.

A plugin ZIP has to have the right shape

WordPress expects the archive to contain a single directory, and that directory contains the plugin. The main PHP file sits at the top of that directory and carries a header comment that WordPress reads to identify the plugin. If the ZIP contains the files at the root, or a folder inside a folder, the install either fails or creates a directory with an unexpected name.

my-plugin.zip
└── my-plugin/
    ├── my-plugin.php        <- main file, with the header
    ├── includes/
    │   └── admin.php
    ├── assets/
    │   └── icon.svg
    ├── readme.txt
    └── uninstall.php

The header is not optional. WordPress reads it to populate the plugin list, and a missing or malformed header produces "The plugin does not have a valid header" even when the file is otherwise fine.

<?php
/**
 * Plugin Name:       My Plugin
 * Description:       A short description of what it does.
 * Version:           1.0.0
 * Requires at least: 6.5
 * Requires PHP:      8.1
 * Author:            Your Name
 * License:           GPLv2 or later
 * Text Domain:       my-plugin
 */

The Plugin Name line is what appears in the list. The Text Domain should match the directory name for translations to load. The licence line matters if the plugin is distributed, and it is worth keeping accurate even for a private plugin, because a future developer will read it as the terms.

Private plugins do not need a readme, a screenshot or a directory listing. They need the same folder shape and the same header, because the install mechanism does not know the difference between a public plugin and an internal one.

The four tools, and when each is the right one

The method is chosen by what failed, not by preference. The table below is the decision guide.

ToolBest forMain constraint
Dashboard uploadAny plugin under the upload limitPHP upload size, nonce timeout
SFTP or SCPLarge ZIPs, no admin accessFile ownership and permissions
WP-CLIRepeatable installs, automationShell access on the host
Host file managerEmergencies, no SSHNo activation step, manual unzip

The dashboard is the default. SFTP is the fallback when the file is large or the admin is unavailable, and it is also the method that teaches the most about how the plugin is stored. WP-CLI is the right tool when the same install happens on several sites, or when the operation has to be scripted. The host file manager is the last resort, because it can place the files but cannot activate the plugin, and the activation step is where half the failures surface.

Uploading by SFTP without breaking ownership

Connect with the host's SFTP details, upload the ZIP to the site's wp-content/plugins directory, and extract it there. If the host does not provide a shell, upload the extracted folder instead of the ZIP, because the file manager may not unzip. Then activate the plugin in the dashboard or with WP-CLI.

scp my-plugin.zip user@example.com:/var/www/site/wp-content/plugins/
ssh user@example.com
cd /var/www/site
unzip wp-content/plugins/my-plugin.zip -d wp-content/plugins/
wp plugin activate my-plugin

The file ownership is where SFTP installs fail. The web server runs as a particular user, and PHP needs to read the plugin files. If the upload lands as a different user with restrictive permissions, the plugin appears in the list but cannot be activated, or activates and cannot read its own includes. Set directories to 755 and files to 644, and match the owner of the existing wp-content directory rather than inventing one. The permissions article covers the diagnosis when the ownership is wrong.

Do not set 777 to make it work. It is the fastest way to make an upload directory executable by anyone, and it hides the real problem, which is a mismatch between the uploader and the web server user.

Uploading with WP-CLI, including from a URL

WP-CLI installs from the directory, a local ZIP or a URL, and activates in the same command. It is the cleanest method when the host provides a shell, and the one that leaves a command in the history for the next person.

wp plugin install my-plugin.zip --activate
wp plugin install my-plugin --activate
wp plugin install https://example.com/downloads/my-plugin.zip --activate
wp plugin list --fields=name,status,version,update

The URL form is useful for a vendor that issues a signed download link, though the link often expires and the command fails with a 403 that looks like a permissions problem. Download the file first and install from the local path when the link is short-lived.

WP-CLI also gives the verification step for free. wp plugin verify-checksums compares a directory plugin against the official checksums, which is how a modified file gets noticed after an incident. It does not work for private plugins, because there are no published checksums to compare against.

The errors you will see

Most upload failures produce one of a small set of messages. The message usually names a symptom rather than the cause, so the table below maps them.

MessageLikely causeFix
The link you followed has expiredUpload exceeded post_max_size, or the nonce expiredRaise limits or use SFTP
Destination folder already existsA directory with the same name is presentDelete the old plugin folder, then retry
The plugin does not have a valid headerMissing or malformed header, or files nested one level too deepFix the header or the ZIP root
Sorry, you are not allowed to install pluginsMissing capability, multisite, or DISALLOW_FILE_MODSUse an administrator or network admin
Could not create directoryDisk full, ownership, or read-only filesystemCheck disk and permissions
Unable to locate WordPress plugin directoryCustom WP_PLUGIN_DIR or a broken pathCheck the constant and the filesystem

Two of those deserve a note. The "destination folder already exists" error is common when the plugin is being reinstalled or updated from a ZIP that includes the same directory name. Deleting the old directory first loses the plugin's settings only if the plugin stores them in files, which is rare; the database keeps the options. The "not allowed" error on a site where the account is an administrator usually means multisite, where only a super administrator can install plugins, or a DISALLOW_FILE_MODS constant in wp-config.php.

Before and after: the checks that matter

Before the upload, take a backup or use staging. A plugin can fail on activation, and the failure can be a fatal error that locks the admin. Staging is the right place when the plugin touches checkout, mail, payments or the database; the staging article covers the safe order.

After activation, check four things. The plugin appears in the list with the expected version. Its settings screen loads without a fatal error. The front end and the admin still load, and the PHP error log has no new entries. If the plugin registers a scheduled task or a custom table, check that the task exists and the table was created.

wp plugin list --status=active --fields=name,version
wp cron event list --fields=hook,next_run_relative
wp db query "SHOW TABLES LIKE 'wp_myplugin%';"
tail -50 wp-content/debug.log

Then the questions that are not technical. Where did the ZIP come from, and is it the vendor's official download? A nulled plugin is a ZIP with the licence check removed and, often, something else added. Does the plugin have a documented update path, or will it be a manual upload every time? On multisite, does it need network activation rather than site activation?

The tooling that makes this easier is mostly free. WP-CLI for installs and verification, SFTP for the fallback, and staging for the test. SiteGround includes staging and a file manager in the panel, which covers the whole workflow without a separate tool. WooCommerce Marketplace is where many paid extensions are purchased and downloaded as ZIPs, with a vendor review that the free directory does not provide; read the licence terms before installing, because a marketplace purchase usually includes a renewal and a domain limit.

Where the paid tooling is unnecessary: a single site that installs a handful of directory plugins a year. There, the dashboard upload and a backup are enough, and the only upgrade worth making is a staging copy for the plugins that touch money. The decision rule is a sentence: if the plugin can break checkout, test it on staging; if it cannot, upload it, activate it, and check the log.

Martin Kiel · @mkiel

WordPress internals

Writes about the load-bearing parts of WordPress: the options table, hook ordering, WP_Query behaviour and the database work that decides whether a plugin scales or falls over.

Continue

Related in Tooling & Workflow