Choosing and Managing Plugins and Themes

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This sub-section’s WordPress bibliography lists the reference material consulted while preparing these pages.

This page was written against WordPress 7.1.3 (released 2026-10-06; 7.1.4, a security release of 2026-10-28, is the version to run in production) and WP-CLI 2.12.0, and checked on 2026-10-28. WordPress 7.1 requires PHP 7.4 at minimum, and PHP 8.3 or newer is recommended. WordPress 7.2 is in beta and planned for 2026-12-08; nothing described here is expected to change with it. Every command below was run against a WordPress 7.1.3 site (wordpress:7.1.3-php8.4-apache with MariaDB 11.8) through the wordpress:cli-2.12.0-php8.4 image, and every output block is real output.

The page is for the people who decide what code runs on a site: the site owner who picks a theme and a handful of plugins, and the administrator who installs, updates and removes them. Writing plugins is covered from Plugin Fundamentals onwards, and themes from Themes Overview. The examples use the Lumen Press site: its own lumen-catalog plugin (the book catalogue), a small lumen-reviews add-on that depends on it, a must-use plugin with the site’s update policy, and two directory plugins (Two Factor and Plugin Check).

What plugins and themes are

A plugin is PHP code (often with JavaScript, CSS and blocks) that adds behaviour through hooks: a post type, a settings screen, a REST route, a block, a spam filter. Any number of plugins can be active at once, and each one runs on every request that loads WordPress. A theme controls presentation: templates, styles, patterns and, for block themes, theme.json. Exactly one theme is active per site, optionally a child theme on top of a parent.

The split matters when choosing: content and features that must survive a redesign (book records, shortcodes, custom roles, blocks the editors rely on) belong in plugins. The theme review rules call this "plugin territory" and do not allow directory themes to register post types, blocks, shortcodes or roles. A theme that does so locks the content to that theme. See Themes Overview and Page Builders and Commercial Themes for the theme side of that decision.

Code reaches a site from four kinds of source:

Source Who checks it How it is updated

WordPress.org Plugin Directory (wordpress.org/plugins/)

Plugins Team review on first submission, automated scans afterwards; not a security audit of every release

Dashboard and WP-CLI, from downloads.wordpress.org; eligible for auto-updates

WordPress.org Theme Directory (wordpress.org/themes/)

Themes Team manual review of new themes against the published requirements, automated checks on updates

Same as plugins

Commercial vendors (sold outside WordPress.org)

The vendor only

The vendor’s own update server, usually after entering a licence key

Custom or private code (lumen-catalog)

The site’s own team (code review, tests, static analysis)

Deployment (Git, CI, Composer), never the dashboard

Vetting a directory listing

Every plugin page in the directory shows the same set of facts in its sidebar, and themes show a similar set. None of them proves quality alone; together they show whether a plugin is maintained and used.

Field What it says Warning signs

Active installations

Sites that reported the plugin as active in the last update check, rounded (for example "5+ million")

Very low numbers for a plugin that handles logins, payments or file uploads

Last updated

When the plugin’s files in the directory last changed (a release or a readme update)

No release for more than a year; a yellow notice "This plugin hasn’t been tested with the latest 3 major releases"

Tested up to

The Tested up to field of readme.txt, set by the author

Two or more major versions behind 7.1 (the Plugins screen then shows "Untested with your version of WordPress")

WordPress version / PHP version

The Requires at least and Requires PHP headers; WordPress refuses to install or activate a plugin whose minimums the site does not meet

A PHP minimum below 7.4 is harmless; a high one rules the plugin out on older hosting

Support threads

"Issues resolved in last two months: N out of M" in the Support box

Many open threads with no author replies

Ratings and reviews

One- to five-star reviews by WordPress.org users

Recent one-star reviews describing breakage after an update

Advanced View, Development

Download history, older versions, the SVN/Trac log and changelog

A closed listing ("This plugin has been closed"), which also means no more updates reach installed copies

Before adding a plugin to Lumen Press, an administrator also checks three things that the listing does not show:

  • What it costs at runtime: whether it loads scripts on every front-end page, adds database tables or schedules cron events. A staging copy with Query Monitor or the browser’s network panel answers this in minutes.

  • What data leaves the site: the directory guidelines forbid contacting external servers without consent, so a plugin that calls a service must say so in its readme (Akismet, for example, sends comments to its servers).

  • Who else can fix it: a plugin maintained by one person is a bigger risk than one with a team or a company behind it.

Plugin Check and Theme Check

Plugin Check (version 2.1.0, a WordPress.org community plugin) runs the automated checks used for new directory submissions plus best-practice checks for internationalisation, accessibility, performance and security. It is a tool for authors, not an audit for buyers, but it is a quick way to see how carefully an off-directory or private plugin was written. It adds Tools > Plugin Check and a WP-CLI command:

wp plugin install plugin-check --activate
wp plugin check lumen-catalog

For the minimal lumen-catalog main file, the only finding is the missing readme, which matters only for the directory:

FILE: readme.txt
line	column	type	code	message	docs
0	0	ERROR	no_plugin_readme	The plugin readme.txt does not exist.

Themes have the equivalent Theme Check plugin, which tests a theme against the Theme Review requirements. Neither tool replaces reading the code of a plugin that will run with administrator privileges; it only catches the obvious. Deactivate and delete both tools on production once the check is done.

Free, freemium and commercial

Older books separate free plugins from "premium" ones. The distinction that matters today is how the directory guidelines treat each model:

Model How it works What to check

Free

Everything is in the directory release; support in the WordPress.org forums

Maintenance signals above

Freemium

A free directory plugin plus a separate paid add-on ("Pro") or a paid external service. Directory plugins may not lock features behind payment, time-limited trials or quotas; a paid service that does substantial work outside the site is allowed if the readme documents it

The paid add-on is a second plugin with its own update channel and licence; upsell notices must be dismissible

Commercial

Sold and downloaded from the vendor’s site; updates through the vendor’s update server with a licence key

Licence renewal (expired licences stop updates, including security fixes), refund and support terms, the vendor’s security disclosure record

All WordPress plugins and themes that run on the site are derivative works of GPL-licensed software. Commercial vendors usually license their PHP under the GPL and sell access to downloads, updates and support rather than the code itself.

Nulled and off-directory code

A nulled plugin or theme is a commercial product with its licence check removed, redistributed for free on download sites. The GPL may allow redistribution, but such copies are a common way to plant back doors: an extra admin user, a remote-code-execution endpoint hidden in an obfuscated file, spam links injected into pages. They also never receive updates, because the vendor’s update server rejects them. The Advanced Administration Handbook’s hardening advice is to get plugins and themes only from WordPress.org or well-known companies. For Lumen Press the rule is simple: code comes from WordPress.org, from a vendor with a contract, or from the team’s own repository, and nothing else.

Private plugins have a different risk: a directory plugin with the same slug. WordPress asks WordPress.org for updates by slug, so if someone publishes a lumen-reviews plugin in the directory, the dashboard would offer it as an "update" of the private one. The Update URI header (5.8) prevents that; any value other than a WordPress.org URL makes WordPress.org ignore the plugin, and an update server can answer through the update_plugins_{$hostname} filter instead.

Installing plugins and themes

There are three routes, and all three end with the files in wp-content/plugins/<slug>/ or wp-content/themes/<slug>/:

  • From the directory: Plugins > Add New Plugin or Appearance > Themes > Add New Theme, search, Install Now, then Activate. Requires that WordPress can write to wp-content (or has FTP/SSH credentials, see File Permissions and Filesystem).

  • By uploading a ZIP: the Upload Plugin or Upload Theme button on the same screens. The archive must contain one top-level folder named after the slug. The upload is limited by PHP’s upload_max_filesize and post_max_size.

  • By SFTP or deployment: copy the extracted folder into wp-content/plugins/ or wp-content/themes/, then activate it. This is the only route when DISALLOW_FILE_MODS is true, and the normal route for Lumen Press’s own code.

DISALLOW_FILE_MODS removes all three dashboard routes, the update buttons and automatic updates of plugins, themes and core (internally, wp_is_file_mod_allowed() returns false). DISALLOW_FILE_EDIT only removes the theme and plugin file editors. One older book describes DISALLOW_FILE_MODS as only disabling updates, which understates it.

The same tasks with WP-CLI, against the Compose stack of Local Development Environments (prefix each command with docker compose run --rm cli there):

# From the directory, activating at once
wp plugin install two-factor --activate

# A specific version (useful to reproduce a bug, or to downgrade)
wp plugin install two-factor --version=0.16.0 --force

# From a ZIP on disk or a URL
wp plugin install wp-content/lumen-reviews.zip

# Integrity check of directory plugins against WordPress.org checksums
wp plugin verify-checksums akismet two-factor
Installing Two Factor (0.17.0)
Downloading installation package from https://downloads.wordpress.org/plugin/two-factor.0.17.0.zip...
Unpacking the package...
Installing the plugin...
Plugin installed successfully.
Activating 'two-factor'...
Plugin 'two-factor' activated.
Success: Installed 1 of 1 plugins.

wp plugin verify-checksums compares every file with the checksums WordPress.org publishes for that version and prints Success: Verified 2 of 2 plugins. when nothing was modified. It is the quickest way to find tampered directory plugins after an incident; it cannot check commercial or private plugins. Themes are installed the same way with wp theme install <slug> --activate.

Plugin dependencies (Requires Plugins)

Since WordPress 6.5, a plugin can declare that it needs other plugins with the Requires Plugins header: a comma-separated list of WordPress.org-style slugs (lumen-catalog, not lumen-catalog/lumen-catalog.php). One older book says plugins cannot depend on each other; that was true of core until 6.5, when this header was added.

lumen-reviews adds reader reviews to the catalogue, so it cannot work without lumen-catalog:

<?php
/**
 * Plugin Name:       Lumen Reviews
 * Description:       Reader reviews for books in the Lumen Press catalogue.
 * Version:           0.1.0
 * Requires at least: 6.9
 * Requires PHP:      7.4
 * Requires Plugins:  lumen-catalog
 * Author:            Lumen Press
 * License:           GPL-2.0-or-later
 * Text Domain:       lumen-reviews
 * Update URI:        https://plugins.lumenpress.example/lumen-reviews/
 */

defined( 'ABSPATH' ) || exit;

The dependency is a plain slug: lumen-catalog is the canonical main plugin of Lumen Press (header Version: 0.1.0, Requires at least: 6.9, Requires PHP: 7.4, Text Domain: lumen-catalog). With lumen-catalog inactive, activation of lumen-reviews fails, in the dashboard and in WP-CLI alike, because both go through activate_plugin():

wp plugin activate lumen-reviews
wp eval 'var_dump( activate_plugin( "lumen-reviews/lumen-reviews.php" ) );'
Warning: Failed to activate plugin. Lumen Reviews requires 1 plugin to be installed and activated: Lumen Catalog. .
Error: No plugins activated.
object(WP_Error)#2586 (3) {
  ["errors"]=>
  array(1) {
    ["plugin_missing_dependencies"]=>
    ...
  ["error_data"]=>
  array(1) {
    ["plugin_missing_dependencies"]=>
    array(1) {
      ["inactive"]=>
      array(1) {
        ["lumen-catalog"]=>
        string(13) "Lumen Catalog"

On the Plugins screen, WP_Plugin_Dependencies adds a "Requires:" line to the dependent and a "Required by:" line to the dependency, disables Activate on the dependent until its dependencies are active, and hides Deactivate and Delete on a dependency while dependents are active (and Delete while any dependent is installed). On Add New Plugin, Install Now is disabled for a plugin whose dependencies are missing, and the card links to each dependency. Nothing is installed automatically: the administrator installs each dependency.

Order matters for WP-CLI, which processes the arguments left to right:

wp plugin activate lumen-reviews lumen-catalog
Warning: Failed to activate plugin. Lumen Reviews requires 1 plugin to be installed and activated: Lumen Catalog. .
Error: Only activated 1 of 2 plugins.
Plugin 'lumen-catalog' activated.

Listing the dependency first (wp plugin activate lumen-catalog lumen-reviews) activates both. The same applies to wp plugin activate --all, which may need to run twice when a dependent sorts before its dependency.

The guard only protects activation and the Plugins screen. wp plugin deactivate lumen-catalog succeeded on the test site while lumen-reviews stayed active, leaving the dependent running without its dependency. A dependent must therefore still check at runtime (function_exists(), class_exists(), a version constant) before calling into the dependency.

Limits of the feature, as documented in the 6.5 announcement:

  • no version constraints (a dependent cannot ask for lumen-catalog 0.2 or newer)

  • no control over load order; plugins still load alphabetically

  • a directory plugin can only depend on directory plugins; a private or commercial dependent may name any slug, but the screen cannot offer an install link for it

  • circular dependencies are detected and the plugins involved cannot be activated

  • must-use plugins are not supported as dependencies, and themes cannot declare Requires Plugins

  • the wp_plugin_dependencies_slug filter maps a slug to another, so a "Pro" edition (my-plugin-pro) can satisfy a dependency on my-plugin

Must-use plugins

A must-use plugin is a PHP file in wp-content/mu-plugins/ (WPMU_PLUGIN_DIR, with WPMU_PLUGIN_URL for its URL). WordPress loads every .php file directly in that directory, in alphabetical order, on every request and before regular plugins, then fires muplugins_loaded. Must-use plugins cannot be deactivated from the dashboard, appear only under Plugins > Must-Use, get no update notices or auto-updates, and never run activation, deactivation or uninstall hooks. Removing the file is the only way to turn one off.

That makes them the right place for site policy that must not be switched off by accident, such as the update rules of Lumen Press. The update filters also belong here rather than in wp-config.php, where they run before plugins and conflict with WP-CLI:

<?php
/**
 * Plugin Name: Lumen Site Policy
 * Description: Site-wide update and plugin policy for Lumen Press. Loaded on every request; cannot be deactivated from the dashboard.
 * Version:     0.1.0
 * Author:      Lumen Press
 * License:     GPL-2.0-or-later
 */

defined( 'ABSPATH' ) || exit;

// Always auto-update the plugins the site depends on, whatever the toggle says.
add_filter(
	'auto_update_plugin',
	static function ( $update, $item ) {
		$always = array( 'akismet', 'two-factor' );
		if ( isset( $item->slug ) && in_array( $item->slug, $always, true ) ) {
			return true;
		}
		return $update;
	},
	10,
	2
);

// Theme updates are reviewed on staging first: never auto-update themes on this site.
add_filter( 'auto_update_theme', '__return_false' );

// Hide the "Enable auto-updates" links for themes, since the filter above overrides them.
add_filter( 'themes_auto_update_enabled', '__return_false' );

Files in subdirectories are not loaded. A must-use plugin kept in its own folder needs a loader file at the top level:

<?php
/**
 * Plugin Name: Load Lumen Observability
 * Description: Loads the must-use plugin kept in its own directory (WordPress only loads PHP files at the top level of mu-plugins).
 * Version:     0.1.0
 */

defined( 'ABSPATH' ) || exit;

require WPMU_PLUGIN_DIR . '/lumen-observability/lumen-observability.php';

The test site had mu-plugins/lumen-observability/lumen-observability.php, which adds an X-Lumen-Env response header from send_headers. Before the loader existed, has_action( 'send_headers' ) was false and the header was missing. With the loader in place:

wp plugin list --status=must-use --fields=name,status,version
curl -sI http://localhost/ | grep -i x-lumen
name	status	version
load-lumen-observability	must-use	0.1.0
lumen-site-policy	must-use	0.1.0
X-Lumen-Env: production

Must-use plugins run on every site of a Multisite network. Because they always run and are not listed with the other plugins, a broken one takes the whole site down and a malicious one is easy to miss; keep them few, short and under version control.

Drop-ins are different: single files directly in wp-content/ with reserved names that replace a part of core. advanced-cache.php (loaded when WP_CACHE is true), object-cache.php, db.php, db-error.php, maintenance.php, php-error.php, fatal-error-handler.php and, on Multisite, sunrise.php (with SUNRISE) and the blog-*.php messages. They appear under Plugins > Drop-ins (wp plugin list --status=dropin), are installed by caching or database plugins, and are covered in Performance and Caching.

Auto-updates

Since WordPress 5.5, each plugin row on Plugins > Installed Plugins has an Enable auto-updates link in the Automatic Updates column, and bulk actions toggle several plugins at once. For themes, the link is in the theme details modal under Appearance > Themes. The choices are stored in two options, auto_update_plugins (plugin files such as two-factor/two-factor.php) and auto_update_themes (theme slugs).

The checks run from WP-Cron: wp_version_check, wp_update_plugins and wp_update_themes are scheduled twice daily, and wp_maybe_auto_update performs the updates. A site with broken WP-Cron (see WP-Cron and Scheduled Tasks) never auto-updates. After each run, the site’s admin email receives a summary of successful and failed updates; auto_plugin_update_send_email and auto_theme_update_send_email turn those emails off.

wp plugin auto-updates enable two-factor
wp theme auto-updates enable twentytwentyfour
wp plugin auto-updates status --all
wp plugin list --fields=name,status,update,auto_update
wp option get auto_update_plugins --format=json
Success: Enabled 1 of 1 plugin auto-updates.
Success: Enabled 1 of 1 theme auto-updates.
name	status
akismet	disabled
hello	disabled
two-factor	enabled
name	status	update	auto_update
akismet	inactive	none	off
hello	inactive	none	off
lumen-catalog	active	none	off
lumen-reviews	active	none	off
two-factor	active	available	on
lumen-site-policy	must-use		off
["two-factor\/two-factor.php"]

update is available because two-factor was installed at 0.16.0 while 0.17.0 is current. lumen-catalog and lumen-reviews show none even though they are not in the directory: WordPress simply received no update for them.

The filters always win over the toggles:

Filter or constant Effect

auto_update_{$type} (auto_update_plugin, auto_update_theme, auto_update_core, auto_update_translation)

Receives the toggle’s value for each item; returning a fixed true or false overrides the administrator’s toggle

plugins_auto_update_enabled, themes_auto_update_enabled

false hides the toggle UI entirely

automatic_updater_disabled filter, AUTOMATIC_UPDATER_DISABLED constant

Turn off all automatic updates, including security releases of core (discouraged)

DISALLOW_FILE_MODS

Turns off all file changes from WordPress, automatic updates included

When a filter forces the result, the Plugins screen shows "Auto-updates enabled" or "Auto-updates disabled" without a link (core calls wp_is_auto_update_forced_for_item()). WP-CLI does not: its auto_update column reports the stored option. On the test site, with the policy plugin above active, akismet was listed as off although wp_is_auto_update_forced_for_item( 'plugin', null, $item ) returned true, and twentytwentyfour was listed as on although the theme filter returns false. Audit a site’s real update behaviour with the filters, not the column.

WordPress.org can also push a security release to every site that runs a vulnerable plugin, independent of the toggle; this is rare and decided by the Plugins Team. The full update model (core majors and minors, translations, WP_AUTO_UPDATE_CORE, version-control checkouts) is in Updates and Auto-Updates.

Rollback when an update fails

Two releases made updates safer:

  • WordPress 6.3, failed manual updates. Before a plugin or theme is replaced, the old copy is moved to wp-content/upgrade-temp-backup/plugins/<slug>/ or .../themes/<slug>/. If the installation itself fails (empty package, unwritable directory, failed copy), the old copy is moved back. A weekly cron event, wp_delete_temp_updater_backups, removes leftovers.

  • WordPress 6.6, failed automatic plugin updates. After auto-updating a plugin that was active, WordPress puts the site in maintenance mode and makes a loopback request to the home page. If that request shows a PHP fatal error, the previous version is restored from the temporary backup and the email reports "The update for '<slug>' contained a fatal error. The previously installed version has been restored."

The limits follow from how it works: inactive plugins are not checked, themes are not checked, errors that are not fatal (a broken layout, a JavaScript error, a failing checkout) are not detected, and a blocked loopback request (see Site Health) makes the check unreliable. Database changes made by the new version’s upgrade routine are not undone. Backups and a staging copy are still needed; see Backups and Disaster Recovery.

Akismet and Hello Dolly

The WordPress 7.1.3 package ships two plugins and three themes, all inactive except the default theme:

wp plugin list
wp theme list
name	status	update	version	update_version	auto_update
akismet	inactive	none	5.7.2		off
hello	inactive	none	1.7.2		off
name	status	update	version	update_version	auto_update
twentytwentyfive	active	none	1.5		off
twentytwentyfour	inactive	none	1.6		off
twentytwentythree	inactive	none	1.7		off
  • Akismet Anti-spam (5.7.2), by Automattic, checks comments and contact-form submissions against the Akismet service. It needs an API key from akismet.com: keys are free for personal blogs, while businesses and commercial sites need a paid subscription. Lumen Press is a commercial site, so "free and preinstalled" is not the same as "free to use" there. Activating it sends comment data to an external service, which belongs in the privacy policy (see SEO, Analytics and Privacy). Its comment-moderation side is covered in Categories, Tags and Comments.

  • Hello Dolly (1.7.2) is a single file, wp-content/plugins/hello.php (slug hello), that shows a random line from the song in the admin header. It is kept as a historical sample of the smallest possible plugin and does nothing useful on a production site.

Core updates do not put them back once deleted: update-core.php copies a bundled plugin only when the site is upgraded from a version older than the one that first bundled it (WordPress 2.0 for Akismet). Deleting them on an existing site is safe and recommended when they are not used. The older default themes (Twenty Twenty-Four, Twenty Twenty-Three) can also go, keeping one default theme as a fallback.

Deactivating, deleting and uninstalling

Action What happens Data

Deactivate

The plugin stops loading; register_deactivation_hook() callbacks run (typically flushing rewrite rules or unscheduling cron). Files stay on disk

Options, tables, post meta and user meta stay

Delete (Plugins screen, after deactivating)

Runs the plugin’s uninstall routine (uninstall.php with WP_UNINSTALL_PLUGIN defined, or the register_uninstall_hook() callback), then deletes the files

Whatever the uninstall routine removes, nothing more

wp plugin uninstall <slug>

Same as Delete in the dashboard

As above

wp plugin delete <slug>

Deletes the files only; no uninstall routine

Everything stays in the database

Removing the folder by SFTP

WordPress notices the plugin is missing and deactivates it on the next Plugins screen load

Everything stays

lumen-reviews ships an uninstall.php:

<?php
// Runs only when the plugin is deleted from the Plugins screen or with `wp plugin uninstall`.
defined( 'WP_UNINSTALL_PLUGIN' ) || exit;

delete_option( 'lumen_reviews_settings' );

The difference between the two WP-CLI commands, on the test site:

wp option add lumen_reviews_settings '{"per_page":10}' --format=json
wp plugin deactivate lumen-reviews
wp plugin delete lumen-reviews
wp option get lumen_reviews_settings --format=json

wp plugin install wp-content/lumen-reviews.zip
wp plugin uninstall lumen-reviews
wp option get lumen_reviews_settings
Success: Added 'lumen_reviews_settings' option.
Plugin 'lumen-reviews' deactivated.
Success: Deactivated 1 of 1 plugins.
Deleted 'lumen-reviews' plugin.
Success: Deleted 1 of 1 plugins.
{"per_page":10}
Unpacking the package...
Installing the plugin...
Plugin installed successfully.
Success: Installed 1 of 1 plugins.
Uninstalled and deleted 'lumen-reviews' plugin.
Success: Uninstalled 1 of 1 plugins.
Error: Could not get 'lumen_reviews_settings' option. Does it exist?

wp plugin delete left the option behind; wp plugin uninstall removed it. Many plugins deliberately keep their data on delete (or ask in their settings), so that a reinstall does not lose content; check the plugin’s documentation before relying on either.

An inactive plugin is not harmless. Its files are still on the server, and a vulnerable file that can be requested directly by URL remains exploitable. The hardening guide’s advice is to delete plugins that are not in use, not just deactivate them.

Themes follow the same rules with two differences: the active theme cannot be deleted, and a parent theme must stay installed while a child theme that uses it is active.

wp theme delete twentytwentyfive
wp theme delete twentytwentythree
Warning: Can't delete the currently active theme: twentytwentyfive
Error: No themes deleted.
Deleted 'twentytwentythree' theme.
Success: Deleted 1 of 1 themes.

Switching themes leaves content in place but can lose presentation: Customizer settings (theme mods) are stored per theme, widgets of a classic theme move to Inactive widgets, and templates edited in the Site Editor (wp_template posts) belong to the theme they were made for. Child themes are covered in Child Themes.

A plugin and theme policy for Lumen Press

The rules the Lumen Press team applies, which work for most sites:

  1. Before installing: check the directory signals, read the readme’s external-services section, run Plugin Check on any code that does not come from the directory, and try it on staging.

  2. Prefer one well-maintained plugin to three overlapping ones; every active plugin adds code to every request.

  3. Enable auto-updates for directory plugins with a good update record; keep the private plugins (lumen-catalog, lumen-reviews) and the theme on the deployment pipeline.

  4. Put update filters and other policy in a must-use plugin under version control.

  5. Record each commercial licence and its renewal date; an expired licence is an unpatched plugin.

  6. Delete, not just deactivate, anything not in use, including Hello Dolly and unused default themes.

  7. Run wp plugin verify-checksums --all and wp plugin list --fields=name,status,update,auto_update after any incident or before an audit.

Pitfalls and corrections to older books

  • Plugins can depend on other plugins. One older book says they cannot; Requires Plugins (6.5) blocks activation until dependencies are active, although it has no version constraints and WP-CLI can still deactivate a dependency.

  • DISALLOW_FILE_MODS is broader than "no updates": it also removes installing, uploading and the file editors, and turns off automatic updates.

  • Akismet is bundled, not free for every site. Personal blogs get a free key; commercial sites need a paid plan.

  • Rollback exists but is narrow. Since 6.3 a failed install restores the old copy, and since 6.6 a fatal error after an automatic update of an active plugin does too; nothing else is rolled back.

  • Deactivating is not removing. Inactive code still sits on the server; delete it.

  • wp plugin delete is not the dashboard’s Delete: only wp plugin uninstall runs the uninstall routine.

  • Must-use plugins in folders are not loaded without a loader file, and they get no updates.

  • Auto-update columns can mislead. A filter in a must-use plugin overrides the toggles; WP-CLI’s auto_update column shows only the stored option.

References