An abandoned WordPress plugin gives you no warning in the admin panel. The plugins screen is an update notification list, not a health report, and a plugin nobody maintains never ships an update. So it sits in that list looking exactly like a healthy plugin.
I did not want to repeat that as folklore, so I went to check it in core. Then I built the two-minute check that actually works.
What the plugins screen actually renders
The file behind that screen is wp-admin/includes/class-wp-plugins-list-table.php. On the master branch of WordPress/WordPress it is 1,744 lines. I grepped it for every word you would expect a warning to contain:
git clone --depth 1 https://github.com/WordPress/WordPress.git
cd WordPress
grep -nEi 'no longer|closed|abandon|unmaintain|removed from the directory|major releases|has not been tested' \
wp-admin/includes/class-wp-plugins-list-table.php
# no output
Zero matches. Same grep against wp-admin/includes/update.php: zero matches.
There is exactly one hit anywhere near this, in wp-admin/includes/plugin-install.php, line 832:
'<strong>Warning:</strong> This plugin <strong>has not been tested</strong> with your current version of WordPress.'
Read where that lives. It belongs to the install flow, the details view you get while adding a new plugin. If a plugin is already installed and running your site, nobody ever opens that screen again.
The warning exists. It just renders on the one page an owner of a finished site never visits.
One endpoint, three answers
Everything you need is in the public directory API. No key, no auth:
https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]=SLUG
Three shapes come back, and the difference between them is the whole check.
Answer 1: still in the directory
curl -s "https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]=search-everything" \
| jq '{last_updated, tested}'
// trimmed to the two fields that decide this
{
"last_updated": "2017-11-28",
"tested": "4.7.35"
}
Search Everything is still listed and still downloadable. Last code change: November 2017. Tested up to WordPress 4.7.35, while current WordPress is 7.1. Ten thousand sites run it.
That gap is wide enough that the directory page prints its own banner:
This plugin hasn't been tested with the latest 3 major releases of WordPress. It may no longer be maintained or supported and may have compatibility issues when used with more recent versions of WordPress.
Note the location again. That banner is on wordpress.org, not in your admin.
Answer 2: closed
curl -s -o body.json \
-w 'HTTP %{http_code}\n' \
"https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]=widget-logic"
# HTTP 404
cat body.json
// trimmed
{
"error": "closed",
"reason_text": "Guideline Violation"
}
Two things worth pinning down here.
First, a trap for anyone scripting this: the closed response arrives with HTTP 404. Branch on the status code alone and you cannot tell "pulled from the directory" from "never existed". Read the body.
Second, the human side. Widget Logic was closed on 2026-04-14, and the closure notice states plainly: "This closure is permanent." More than 100,000 sites are still running it four months later. Not one of those admin panels mentions it.
Answer 3: not found
{ "error": "Plugin not found." }
This plugin was never in the directory. It is commercial, or custom built for that site. The directory has nothing to tell you, so the question moves to the vendor or to your own repo.
Check the whole list, not the one plugin you suspect
The check is cheap enough to run across everything installed:
#!/usr/bin/env bash
API="https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]="
wp plugin list --field=name | while read -r slug; do
body=$(curl -s "${API}${slug}")
err=$(printf '%s' "$body" | jq -r '.error // empty')
case "$err" in
"")
printf '%-28s OK last_updated=%s tested=%s\n' "$slug" \
"$(printf '%s' "$body" | jq -r '.last_updated')" \
"$(printf '%s' "$body" | jq -r '.tested')"
;;
"closed")
printf '%-28s CLOSED reason=%s\n' "$slug" \
"$(printf '%s' "$body" | jq -r '.reason_text // "not disclosed yet"')"
;;
*)
printf '%-28s NOT IN DIRECTORY\n' "$slug"
;;
esac
sleep 1
done
Twenty plugins, one coffee, once a year.
reason_text is a triage signal, not trivia
The plugin developer FAQ states the closure reasons directly: "Plugins are closed for guideline violations, security issues, or by author requests."
That maps cleanly onto your calendar:
- Security Issue: a hole was found. Take it off today.
- Guideline Violation: rules were broken. Find out what happened, plan the swap.
- Author Request: the author asked out. You have room for a calm migration.
Two caveats from the same FAQ, and both matter more than the mapping.
The reason is only published after 60 days, and even then "only in the broadest terms". A plugin closed last month tells you nothing about why. Treat that silence as urgent rather than harmless.
And closures are not always a dramatic event. The directory can close a plugin on its own:
If a plugin has never been used within 6 months (i.e. no code has been pushed to SVN), SVN is broken for upwards of 12 months, or a plugin's readme indicates it's deprecated, we may close without notification.
The case that breaks the heuristic
If you audit only on tested, you will miss the most honest case of all.
Database Backup for WordPress (wp-db-backup) has 60,000 installs and is tested up to 6.9.7. That version sits inside the last three major releases, so the directory banner has no reason to fire. Everything looks current.
The author's own description says:
Database Backups for WordPress is no longer actively maintained... No additional releases, including security releases, will be made available.
A tested header tells you somebody bumped a number. The description is where authors say they are done. Read both.
Deactivating is not removing
This is where people lose sites. Deactivating a plugin does not take its files off the server.
Core says as much, in wp-admin/includes/class-wp-site-health.php at lines 462 and 475:
'You should remove inactive plugins'
'Inactive plugins are tempting targets for attackers. If you are not going to use a plugin, you should consider removing it.'
Take a backup before you delete anything. Deleting through the admin can run the plugin's uninstall routine, and that is not something you undo.
What the numbers say, and what they do not
Patchstack's "State of WordPress Security in 2026", covering 2025:
- 11,334 new vulnerabilities were found in the WordPress ecosystem in 2025
- 91% of new vulnerabilities were found in plugins
- 46% of vulnerabilities did not receive a patch by the time of disclosure
- the weighted median time to first exploit is 5 hours
Be careful with the second number, because it is the one everybody misquotes. That 91% covers plugins as a whole, actively maintained ones included. The report does not isolate abandoned plugins, so neither will I.
The line you can draw is narrower, and it comes from the fourth number. Forty-six percent unpatched at disclosure describes a race that a maintained plugin can still win, because someone is on the other end shipping a fix within those five hours. For a plugin with no maintainer, unpatched is not a delay in that race. It is the finish line.
Takeaways
- The plugins screen answers "did anything arrive", not "is this healthy".
- One endpoint, three answers. The closed answer arrives as HTTP 404, so read the body, not the status.
-
testedis a header somebody bumped. The description is where maintainers announce they are done. - Deactivated still means present on disk.
- Run the check across every plugin, on a schedule, not on a hunch.
Originally published on mrdesigngarwolin.pl - my Polish blog about WordPress, web design, and SEO for small businesses.
Top comments (0)