Before anything is written

Twelve ways an archive gets refused.

A batch installer that trusts its input is a batch installer that eventually unpacks something horrible into wp-content. Every archive goes through the whole list first, and a refusal names its own reason rather than reporting a generic failure.

STRUCTURE

One plugin per archive, and the main file where it belongs

Exactly one top-level folder, a valid folder name, a real WordPress plugin header, and the main plugin file sitting directly inside that folder rather than buried a level deeper. A ZIP of three plugins is refused rather than half-unpacked, and a ZIP of a ZIP is caught here.

PATHS

Traversal and symlinks are rejected, not sanitised

Any entry carrying a parent-directory segment is refused outright. Any unsafe path is refused. A symbolic link inside the archive is refused where the ZIP reader can see it. Refusal rather than repair is deliberate: quietly rewriting a hostile path leaves you trusting the rewrite.

SIZE

A 256 MB uncompressed ceiling

Compression ratios are a weapon as well as a convenience, so the check is on the uncompressed total rather than the file you uploaded. An empty archive, or one with an implausible number of entries, is refused on the same pass.

FIT

Requirements checked against this server, not in general

The declared PHP and WordPress minimums are read from the plugin header and compared against what is actually running. The refusal is specific: “requires PHP 8.2 or later; this server runs 8.0”. That is a sentence you can act on, unlike a white screen after activation.

FORMAT

ZipArchive where it exists, PclZip where it does not

ZipArchive is the preferred reader and is present on nearly every host. Where it is absent the plugin uses PclZip, which WordPress bundles, and symlink detection is weaker on that route. We would rather write that down than let you assume the two paths are identical.

HANDOVER

WordPress does the installing

Once an archive passes, it goes to WordPress's own upgrader APIs rather than to any unpacking code of ours. The preflight is the product; the install is core's job. Anything that installs through Add New, Upload Plugin installs here.

Each refusal message, and what to do about it, is written out one by one in the documentation.

Get the plugin How a run works