Skip to content
All writing

What shipping a plugin to WordPress.org actually takes

· Updated · Engineering
Ask AI

Everyone who has used WordPress has used something out of the plugin directory. Almost nobody who hasn’t shipped one knows what getting in there actually requires. I found out this year at WPAnchorBay, taking ProductBay, a B2B and wholesale product-table plugin for WooCommerce, from a blank repo to a directory submission.

It’s not a marketplace, it’s a review

The WordPress.org Plugin Directory looks like a marketplace. Search, categories, ratings, install counts. Getting into it works nothing like one.

There’s no listing fee and no algorithm to game. There’s a human reviewer reading your code against a specific published set of guidelines, who either passes it or sends it back with itemised objections. You fix every one and resubmit. No partial credit, no shortcut.

What the review actually checks

Three things dominate.

Security. Every piece of user input, so $_POST and $_GET and $_REQUEST and anything arriving over a REST or AJAX endpoint, has to be sanitised on the way in and escaped on the way out. Every action that changes data needs a capability check, and state-changing requests need a nonce. You can’t satisfy this superficially. The reviewer reads the code path rather than the plugin’s description of itself.

Licensing. Everything the plugin ships has to be GPL-compatible, and that includes any bundled library, font or icon set. Bundle a JavaScript library under an incompatible licence and you get rejected regardless of how good the plugin itself is.

Honesty. The readme.txt has to describe what the plugin actually does and no more. Overselling features, claiming compatibility you haven’t verified, or shipping upsells inside the free plugin in a way that makes the free version feel deliberately crippled will all get flagged.

Preparing ProductBay for it

ProductBay’s backend is plain PHP integrating with WooCommerce and WordPress hooks. The admin UI and the front-end product table are React and TypeScript built with @wordpress/scripts.

Getting that codebase ready meant a specific pass rather than a general cleanup: every REST route re-checked for capability enforcement, every table query re-checked for injection surface, every setting re-checked for sanitisation on save and escaping on output.

The React and TypeScript half doesn’t get a pass just because the risky-looking code is “on the backend”. Anything the frontend sends to a REST endpoint is still user input by the time it reaches PHP. The trust boundary is the server, not whichever language happens to sit on either side of the request.

Approval isn’t the finish line

Passing review gets you an SVN repository rather than a git one. WordPress.org still runs on Subversion, and that’s a genuine adjustment if your entire workflow up to that point has been git.

Releases live in a trunk folder plus a tags folder per version. The readme.txt’s Stable tag field is what actually tells WordPress installs which tagged version to serve. Banner and icon assets ship from a separate assets folder outside the plugin code entirely. Getting a release out means developing in git as usual, then mirroring a clean versioned snapshot into that SVN structure. A small extra step, but one that has to be exactly right.

The mechanics look roughly like this. Develop in git, then publish a snapshot to SVN:

Stable tag: 1.3.3
Requires at least: 6.4
Tested up to: 6.8
Requires PHP: 7.4

The parts nobody mentions going in

A few things weren’t obvious to me beforehand.

The reviewer is adversarial on purpose, and that turns out to be the value. For most solo-built plugins this is the only outside security review they will ever get. Treat the objections as a free audit rather than an obstacle to argue with.

“Free” has to mean free, properly. A free plugin that mainly funnels users toward a paid upsell gets flagged in review, and users can tell anyway, so it shows up in the ratings either way. ProductBay’s free edition has to stand on its own. Pro is additive rather than a repair for a gap left there deliberately.

And the readme.txt is a product surface, not paperwork. It’s the first thing a prospective user reads, and the review process treats accuracy in it as seriously as it treats the code.

What it taught me

The review isn’t a gate standing between me and distribution. It’s a second independent pass over the same security assumptions I’m supposed to be making anyway, done by someone with no context on my code and no incentive to be generous about it.

Every plugin I ship from here gets built as though that reviewer is already reading it, because eventually one will be.

Share this post