Every plugin that loads a stylesheet or a script writes its own directory name into the page's HTML. Reading them takes one lookup. Understanding why the resulting list is always shorter than the real one takes slightly longer, and it is the part that stops you drawing a wrong conclusion.
The quick way
Run the URL through the WordPress Theme Detector, which reports plugins alongside the theme. It collects every slug it finds under /wp-content/plugins/, then looks each one up in the WordPress.org plugin directory to get the published name, current version and the date it was last updated.
That lookup is the difference between a directory listing and useful information. A slug tells you a plugin is installed; the last-updated date tells you whether anyone is still maintaining it.
Reading it yourself
View the page source and search for wp-content/plugins. The segment immediately after is the slug:
<link rel="stylesheet" href="/wp-content/plugins/contact-form-7/includes/css/styles.css?ver=6.0.1">
<script src="/wp-content/plugins/woocommerce/assets/js/frontend/cart-fragments.min.js"></script>
That is contact-form-7 and woocommerce. The ?ver= on these files is the plugin's version, not WordPress's — a distinction worth keeping straight if you are also checking the core version.
Two other places worth searching, both of which survive some of the things that hide asset paths:
Stylesheet handles. WordPress puts an id on enqueued styles, built from the handle the plugin registered:
<style id="contact-form-7-inline-css">
These often remain even when a caching plugin has merged the underlying files, because the inline block is generated separately from the ones that got combined.
The REST API namespace list. Open https://example.com/wp-json/ and read the namespaces array. Plugins that register routes appear there by name, and this route is served by WordPress itself rather than by the asset pipeline, so no amount of CDN or minification touches it.
Why the list is always incomplete
This is the limitation that matters, and it is not a flaw in any particular tool — it is a property of looking at a site from outside.
Only plugins that load something on the page you checked can appear. A plugin that does its work in the admin, on a cron schedule, or on the server before the page is built emits nothing into the HTML. That covers most SEO plugins, every backup plugin, security plugins, migration tools, staging tools, redirect managers and analytics integrations that inject nothing client-side. On a typical site these are the majority of what is installed.
The page you picked decides the answer. A contact form plugin loads its assets on the contact page and nowhere else. A gallery plugin appears only where a gallery exists. Checking the home page alone routinely misses half of a site's front-end plugins — run the check again on a contact page, a blog post and a product page, and combine the results.
Optimisation plugins merge the evidence away. When several stylesheets are combined into one file served from a cache directory, the individual plugin paths stop existing:
<link rel="stylesheet" href="/wp-content/cache/min/1/a3f9c2.css">
There is a useful inference in that URL, though: the cache directory itself names the plugin doing the caching. wp-rocket, litespeed, autoptimize, wpfc-minified and breeze all announce themselves this way, so the plugin that hid the others usually identifies itself in the process.
Premium plugins have no public record. The directory lookup only covers plugins published on WordPress.org. A commercial plugin returns nothing, so you get its slug and no name, version or maintenance date. The slug is usually still readable enough to search for.
The honest summary: a plugin found this way is definitely installed. A plugin not found may be installed, or may not be, and the two cases are not distinguishable from outside. Any tool that presents an external scan as a complete inventory is overstating what it measured.
The last-updated date is the point
For a site you own or are evaluating, this is the most useful column and it is the one people skim past.
A plugin whose last release was more than two years ago is receiving no security fixes. WordPress.org marks plugins untested with recent releases, and a meaningful share of successful attacks on WordPress installs arrive through an abandoned plugin rather than through core — core updates itself; plugins wait for someone to notice.
Worth checking in this order:
- Abandoned. No update in two years or more. Find a maintained replacement, or accept a known and growing risk deliberately.
- Installed but not used. Deactivated plugins still have their code on disk and are still reachable in some vulnerability classes. Delete rather than deactivate.
- Overlapping. Two plugins doing the same job is common after a site changes hands, and it is a cheap thing to fix.
Checking a site you own
Externally you are guessing about the back end. Internally you are not.
Plugins → Installed Plugins in the admin lists everything with its version, and flags what has an update waiting.
On the command line, WP-CLI gives you the same thing in a form you can act on:
wp plugin list --status=active
wp plugin list --update=available
Do not forget must-use plugins. Anything in wp-content/mu-plugins/ loads automatically, cannot be deactivated from the admin, and appears under a separate Must-Use tab that is easy to miss. Managed hosts commonly install their own there, and so do some migration tools, which is how a site ends up with code nobody remembers adding. Externally these are essentially invisible.
Can a site hide which plugins it uses?
Less completely than it can hide the version, and more completely than it can hide that it runs WordPress.
Merging and minifying assets removes most plugin paths as a side effect of a change made for performance, which is the main reason plugin lists come back short. A CDN with rewritten paths does the same. A few security plugins rewrite plugin URLs deliberately.
What survives is harder to reach: the markup the plugin generates, the class names on it, the inline style and script handles, the REST API namespaces, and any front-end behaviour distinctive enough to recognise. A site determined to remove all of that would be rewriting each plugin's output, which nobody does.
As with the version number, obscurity is not the control that helps here. An automated scanner probing for a vulnerable plugin requests that plugin's known paths directly and reads the response; it never consults your home page HTML. Keeping plugins current, deleting the ones you do not use, and limiting how many you install do the actual work.
What to do with the list
If you were looking because a site does something you want, the slug is the answer — search it and you will find the plugin, its price if it has one, and whether it is still maintained. Check that last part before you install it.
If you were looking at your own site, treat the result as the list of plugins you are publishing to anyone who looks. Everything visible there is a component whose name and version a scanner already has. That is not a reason for alarm, and it is a good prompt to check whether each one is current and whether you still need it.
Run any URL through the WordPress Theme Detector, which reports the theme and the plugins together.