<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Muhammad Zeeshan Sardar</title>
    <description>The latest articles on DEV Community by Muhammad Zeeshan Sardar (@zeeshansardar08).</description>
    <link>https://dev.to/zeeshansardar08</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3815588%2F642e7ea5-a321-4d5b-abf8-d4a1c64a51b7.png</url>
      <title>DEV Community: Muhammad Zeeshan Sardar</title>
      <link>https://dev.to/zeeshansardar08</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zeeshansardar08"/>
    <language>en</language>
    <item>
      <title>The Obvious Way to Detect WordPress Plugins Counts WooCommerce Six Times</title>
      <dc:creator>Muhammad Zeeshan Sardar</dc:creator>
      <pubDate>Wed, 30 Sep 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/zeeshansardar08/the-obvious-way-to-detect-wordpress-plugins-counts-woocommerce-six-times-45eo</link>
      <guid>https://dev.to/zeeshansardar08/the-obvious-way-to-detect-wordpress-plugins-counts-woocommerce-six-times-45eo</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Asset paths, script handles and REST namespaces all reveal WordPress plugins from outside, and each one produces false positives if you trust it on its own. Deny core handles, map handle families to their parent plugin, require corroboration, and never trust a &lt;code&gt;?ver=&lt;/code&gt; that matches the core version.&lt;/p&gt;

&lt;p&gt;Point a naive plugin detector at a WooCommerce store and it will tell you the site runs &lt;code&gt;wc&lt;/code&gt;, &lt;code&gt;wc-admin&lt;/code&gt;, &lt;code&gt;wc-analytics&lt;/code&gt;, &lt;code&gt;wc-telemetry&lt;/code&gt;, &lt;code&gt;wccom-site&lt;/code&gt; and &lt;code&gt;wc-admin-email&lt;/code&gt;. Six plugins. It is one plugin, WooCommerce, and the real &lt;code&gt;woocommerce&lt;/code&gt; slug may not appear in the list at all.&lt;/p&gt;

&lt;p&gt;The same detector will report &lt;code&gt;wp-block-editor&lt;/code&gt; and &lt;code&gt;wp-site-health&lt;/code&gt; as plugins. Both are WordPress core.&lt;/p&gt;

&lt;p&gt;I build WordPress plugins, and I recently built a free scanner that reads a public site and lists the plugins it runs, with the maintenance facts WordPress.org publishes about each one. The use case is quoting: before a client hands over a login, you want to know whether you are walking into twelve current plugins or four that have not shipped a release since 2022.&lt;/p&gt;

&lt;p&gt;Getting that list right turned out to be the whole job. Detection from outside is mostly an exercise in not believing your own evidence too quickly. Here are the three signals that work, the trap in each one, and the rules that keep the list honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal 1: asset paths (the one to trust most)
&lt;/h2&gt;

&lt;p&gt;Plugins live in &lt;code&gt;/wp-content/plugins/&amp;lt;slug&amp;gt;/&lt;/code&gt;, and when a plugin loads a stylesheet, script or image on a page, that path shows up in the HTML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PLUGIN_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;wp-content&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;plugins&lt;/span&gt;&lt;span class="se"&gt;\/([&lt;/span&gt;&lt;span class="sr"&gt;a-z0-9-&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)\/&lt;/span&gt;&lt;span class="sr"&gt;/gi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;slugsFromPaths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slugs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PLUGIN_PATH&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;slugs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;slugs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a slug appears in an asset path, the plugin is installed and active on that page. That is about as strong as outside evidence gets, and the directory name matches the WordPress.org slug for anything installed from the directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The trap:&lt;/strong&gt; absence proves nothing. Three things hide plugins from this signal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Optimisation and caching plugins&lt;/strong&gt; that combine every stylesheet and script into one bundle served from their own cache folder. The individual plugin paths disappear from the HTML.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugins with no front-end assets.&lt;/strong&gt; Anything that works entirely in wp-admin or on the server leaves no trace in the page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security plugins&lt;/strong&gt; that rewrite or hide plugin paths on purpose.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So a short list from this signal is evidence of what is visible, not evidence of a small install. That distinction has to reach the person reading the report, or they will draw the wrong conclusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal 2: script and style handles (useful, and full of traps)
&lt;/h2&gt;

&lt;p&gt;When WordPress prints an enqueued script or stylesheet, it adds an ID built from the handle the code registered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"contact-form-7-css"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"wc-add-to-cart-js"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Strip the &lt;code&gt;-js&lt;/code&gt; or &lt;code&gt;-css&lt;/code&gt; suffix and you have the handle. This signal matters because it survives some of the situations that hide asset paths, including assets served from a CDN under a different URL structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HANDLE_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;(?:&lt;/span&gt;&lt;span class="sr"&gt;script|link&lt;/span&gt;&lt;span class="se"&gt;)\b[^&lt;/span&gt;&lt;span class="sr"&gt;&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;id=&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;"'&lt;/span&gt;&lt;span class="se"&gt;]([&lt;/span&gt;&lt;span class="sr"&gt;a-z0-9_-&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;js|css&lt;/span&gt;&lt;span class="se"&gt;)[&lt;/span&gt;&lt;span class="sr"&gt;"'&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/gi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handlesFromIds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HANDLE_ID&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;handles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;handles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The regex requires the closing quote straight after &lt;code&gt;-js&lt;/code&gt;, which quietly skips inline companions such as &lt;code&gt;wc-add-to-cart-js-extra&lt;/code&gt; and &lt;code&gt;-js-after&lt;/code&gt;. Those belong to a handle you have already counted.&lt;/p&gt;

&lt;p&gt;This is where the six WooCommerce rows come from. A handle is not a slug. It is whatever string the developer chose, and three kinds of handle break the naive approach. (Core packages also start with &lt;code&gt;wp-&lt;/code&gt;, so a blanket "anything beginning with wp-" rule would throw away real plugins such as &lt;code&gt;wp-mail-smtp&lt;/code&gt;. The deny list has to be explicit.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core handles.&lt;/strong&gt; WordPress registers dozens of its own scripts: &lt;code&gt;wp-block-editor&lt;/code&gt;, &lt;code&gt;wp-polyfill&lt;/code&gt;, &lt;code&gt;jquery-core&lt;/code&gt;, &lt;code&gt;wp-emoji-release&lt;/code&gt;, &lt;code&gt;wp-block-library&lt;/code&gt;, &lt;code&gt;global-styles&lt;/code&gt;, &lt;code&gt;classic-theme-styles&lt;/code&gt; and more. None of them is a plugin. You need an explicit deny list, and it needs a comment saying where it came from, because it will need extending every few releases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handle families.&lt;/strong&gt; One plugin often registers many handles under a shared prefix. WooCommerce uses &lt;code&gt;wc-*&lt;/code&gt; and &lt;code&gt;wccom-*&lt;/code&gt;. Yoast uses &lt;code&gt;yoast-*&lt;/code&gt; and &lt;code&gt;wpseo-*&lt;/code&gt;, and its directory slug is &lt;code&gt;wordpress-seo&lt;/code&gt;, which no handle contains. You need a maintained map from prefix to parent slug, and you emit one row for the parent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Theme handles.&lt;/strong&gt; Themes enqueue assets too. A theme stylesheet with the ID &lt;code&gt;acme-siteheader-css&lt;/code&gt; produces the handle &lt;code&gt;acme-siteheader&lt;/code&gt;, which looks exactly like a plugin that does not exist.&lt;/p&gt;

&lt;p&gt;The rule that fixes all three is corroboration. A slug derived only from a handle is accepted when it resolves in the WordPress.org directory, or when the same slug also appears as a real asset path on the site. Anything else is dropped silently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Core script and style handles. Extend this as WordPress adds packages.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CORE_HANDLES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jquery&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jquery-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jquery-migrate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wp-polyfill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wp-emoji-release&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wp-block-library&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wp-block-editor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wp-site-health&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;global-styles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;classic-theme-styles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin-bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;regenerator-runtime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;moment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Prefix to parent slug. Order matters: more specific prefixes first.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HANDLE_FAMILIES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wccom-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;woocommerce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wc-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;woocommerce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wpseo-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wordpress-seo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yoast-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wordpress-seo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;elementor-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;elementor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;familyParent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;woocommerce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;HANDLE_FAMILIES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;acceptHandle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;pathSlugs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;directorySlugs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CORE_HANDLES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;familyParent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pathSlugs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;directorySlugs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dropping silently is the important part. The tempting alternative is to show unmatched handles as "not in directory". That turns a theme stylesheet into a finding, and a WordPress developer will spot it in the first ten seconds and stop trusting everything else on the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal 3: REST API namespaces
&lt;/h2&gt;

&lt;p&gt;Most WordPress sites expose an index at &lt;code&gt;/wp-json/&lt;/code&gt;, and its &lt;code&gt;namespaces&lt;/code&gt; array lists the route prefixes every active plugin has registered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"namespaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"oembed/1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wc/v3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yoast/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"contact-form-7/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wp/v2"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This catches plugins that load nothing on the front end but still register routes, which fills part of the gap Signal 1 leaves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The trap:&lt;/strong&gt; namespaces are not slugs either. &lt;code&gt;wp/v2&lt;/code&gt; and &lt;code&gt;oembed/1.0&lt;/code&gt; are core. &lt;code&gt;yoast/v1&lt;/code&gt; belongs to &lt;code&gt;wordpress-seo&lt;/code&gt;. &lt;code&gt;wc/v3&lt;/code&gt; belongs to &lt;code&gt;woocommerce&lt;/code&gt;. The same family map from Signal 2 does most of the work here, and the same corroboration rule applies.&lt;/p&gt;

&lt;p&gt;It is also the weakest of the three signals. A namespace proves the plugin registered routes. It does not prove the plugin does anything visible, and some sites disable or restrict the REST index. When you report a plugin, record which signal found it (path, handle, namespace, or several), so you can tell later how much weight each row deserves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting the installed version
&lt;/h2&gt;

&lt;p&gt;Knowing a plugin is installed is half of it. Knowing the site runs 4.2 while the current release is 6.1 is the part that matters for a quote.&lt;/p&gt;

&lt;p&gt;The best source is the plugin's own &lt;code&gt;readme.txt&lt;/code&gt;, fetched from &lt;code&gt;/wp-content/plugins/&amp;lt;slug&amp;gt;/readme.txt&lt;/code&gt;, which carries a &lt;code&gt;Stable tag&lt;/code&gt; line. Many sites block direct requests to plugin folders, and larger ones block them almost always.&lt;/p&gt;

&lt;p&gt;The fallback is the &lt;code&gt;ver&lt;/code&gt; query string on asset URLs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;versionFromAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;pageUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;coreVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageUrl&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ver&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coreVersion&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ver&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;coreVersion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ver&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core version check exists because of how &lt;code&gt;wp_register_script&lt;/code&gt; behaves. When a plugin registers an asset without passing its own version, WordPress appends the core version instead. Read that at face value and you will report that a plugin is on version 6.8, when 6.8 is simply the WordPress version.&lt;/p&gt;

&lt;p&gt;Two more rules make this usable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Label it.&lt;/strong&gt; A version from a readme is confirmed. A version from &lt;code&gt;?ver=&lt;/code&gt; is inferred, because it is a cache-busting parameter and a site can put anything in it. The report should say which is which.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find the core version from core assets&lt;/strong&gt;, such as the &lt;code&gt;ver&lt;/code&gt; on files under &lt;code&gt;/wp-includes/&lt;/code&gt;. The generator meta tag is often removed, so it cannot be the only source.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the directory can tell you, and what it cannot
&lt;/h2&gt;

&lt;p&gt;Once you have a clean list of slugs, the WordPress.org plugin API gives you the maintenance facts for each one: when it last shipped a release, which WordPress version its author last tested against, and whether the directory still lists it.&lt;/p&gt;

&lt;p&gt;What it does not give you is vulnerability data. I made a deliberate decision not to guess at it. Good vulnerability data for WordPress plugins sits behind commercial APIs, and a free tool that fakes it has two options. It can guess from version numbers, which flags plugins that are fine. Or it can flag a few famous cases and stay quiet on the rest, which implies everything unflagged has been checked. Both are worse than saying clearly that no security check was run.&lt;/p&gt;

&lt;p&gt;The same principle applies to the score. If a site loads 11 plugins and 8 of them are premium plugins sold outside the directory, there is no public release history to judge those 8 on. A score built on the remaining 3 should say so, rather than printing "Healthy" next to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The standard I hold a scanner to
&lt;/h2&gt;

&lt;p&gt;Fewer, truer findings beat a longer list. Every rule above removes rows rather than adding them: the deny list, the family map, the corroboration check and the core version check. Each one exists because a plausible-looking false positive costs more trust than a missing row.&lt;/p&gt;

&lt;p&gt;If you want to see the result on a real site, the scanner is free and needs no signup: &lt;a href="https://zignites.com/tools/wordpress-plugin-risk-report" rel="noopener noreferrer"&gt;WordPress Plugin Checker&lt;/a&gt;. It marks which installed versions are inferred rather than confirmed, and states plainly what it could not see. If you only care about one plugin, the &lt;a href="https://zignites.com/plugin-check" rel="noopener noreferrer"&gt;plugin maintenance check&lt;/a&gt; covers widely installed plugins one at a time.&lt;/p&gt;

&lt;p&gt;If you build plugins yourself, I wrote about &lt;a href="https://zignites.com/blog/wordpress-plugin-development-guide" rel="noopener noreferrer"&gt;what maintaining one actually involves&lt;/a&gt;, including why "tested up to" goes stale on its own.&lt;/p&gt;

&lt;p&gt;I would like to hear from anyone who has built detection like this. Which handle families or namespaces have caught you out? The family map only gets better with more eyes on it.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>showdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Export Installed WordPress Plugins &amp; Themes to CSV (Developer-Friendly Method)</title>
      <dc:creator>Muhammad Zeeshan Sardar</dc:creator>
      <pubDate>Mon, 09 Mar 2026 23:08:21 +0000</pubDate>
      <link>https://dev.to/zeeshansardar08/export-installed-wordpress-plugins-themes-to-csv-developer-friendly-method-2cpa</link>
      <guid>https://dev.to/zeeshansardar08/export-installed-wordpress-plugins-themes-to-csv-developer-friendly-method-2cpa</guid>
      <description>&lt;p&gt;If you manage WordPress websites, you've probably encountered this problem.&lt;/p&gt;

&lt;p&gt;You open a project and need to quickly answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What plugins are installed on this site?&lt;/li&gt;
&lt;li&gt;Which theme is active?&lt;/li&gt;
&lt;li&gt;How can I export this list for documentation or auditing?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Surprisingly, WordPress does not provide a built-in way to export installed plugins and themes.&lt;/p&gt;

&lt;p&gt;For developers and agencies managing multiple projects, this can become frustrating.&lt;/p&gt;

&lt;p&gt;So I built a small tool to solve this problem.&lt;/p&gt;

&lt;p&gt;In this article, I’ll show you how to export installed WordPress plugins and themes in seconds.&lt;/p&gt;

&lt;p&gt;If you just want the quick solution:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Site Extensions Snapshot&lt;/li&gt;
&lt;li&gt;Go to Tools → Site Extensions Snapshot&lt;/li&gt;
&lt;li&gt;Click Export to CSV
You will instantly download a list of all installed plugins and themes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Plugin link:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://wordpress.org/plugins/site-extensions-snapshot/" rel="noopener noreferrer"&gt;https://wordpress.org/plugins/site-extensions-snapshot/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Developers Need a Plugin Snapshot
&lt;/h2&gt;

&lt;p&gt;In real-world development, having a snapshot of installed extensions is extremely useful.&lt;/p&gt;

&lt;p&gt;Here are a few common scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client Documentation&lt;/strong&gt;&lt;br&gt;
When handing over a project, you may need to document all plugins and themes used in the website.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website Audits&lt;/strong&gt;&lt;br&gt;
Security or performance audits often require reviewing installed plugins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Troubleshooting&lt;/strong&gt;&lt;br&gt;
Plugin conflicts are common in WordPress. Having a full list helps identify potential issues faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Migration Preparation&lt;/strong&gt;&lt;br&gt;
Before migrating a website, it's helpful to know exactly which extensions are installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with WordPress
&lt;/h2&gt;

&lt;p&gt;WordPress lets you view plugins inside the dashboard, but it doesn't allow you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Export the plugin list&lt;/li&gt;
&lt;li&gt;Download the plugin inventory&lt;/li&gt;
&lt;li&gt;Share it with a team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most developers end up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually copying plugin names&lt;/li&gt;
&lt;li&gt;Taking screenshots&lt;/li&gt;
&lt;li&gt;Writing custom scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This wastes time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Site Extensions Snapshot
&lt;/h2&gt;

&lt;p&gt;To make this easier, I built a small WordPress plugin called:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Site Extensions Snapshot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It allows developers to export installed plugins and themes into a CSV file with one click.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plugin page:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://wordpress.org/plugins/site-extensions-snapshot/" rel="noopener noreferrer"&gt;https://wordpress.org/plugins/site-extensions-snapshot/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Plugin Does
&lt;/h2&gt;

&lt;p&gt;Once installed, the plugin allows you to:&lt;/p&gt;

&lt;p&gt;✔ Export installed plugins to CSV&lt;br&gt;
✔ Export installed themes to CSV&lt;br&gt;
✔ Create a quick snapshot of the site environment&lt;br&gt;
✔ Use the exported list for documentation or audits&lt;/p&gt;

&lt;p&gt;It's a lightweight utility tool designed to save developers time.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use the Plugin
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Install the Plugin&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to your WordPress dashboard:&lt;br&gt;
Plugins → Add New&lt;br&gt;
Search for:&lt;br&gt;
Site Extensions Snapshot&lt;br&gt;
Install and activate it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Export Installed Plugins &amp;amp; Themes&lt;/strong&gt;&lt;br&gt;
After activating the plugin:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to Tools&lt;/li&gt;
&lt;li&gt;Click Site Extensions Snapshot&lt;/li&gt;
&lt;li&gt;Click Export to CSV
That's it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The plugin will download a CSV file containing all installed plugins and themes.&lt;br&gt;
You can now use this file for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Website audits&lt;/li&gt;
&lt;li&gt;Migration planning&lt;/li&gt;
&lt;li&gt;Team collaboration&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who This Plugin Is For
&lt;/h2&gt;

&lt;p&gt;This tool is useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress developers&lt;/li&gt;
&lt;li&gt;Freelancers&lt;/li&gt;
&lt;li&gt;Agencies&lt;/li&gt;
&lt;li&gt;Website auditors&lt;/li&gt;
&lt;li&gt;DevOps teams managing WordPress environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you work with multiple WordPress projects, this tool can simplify documentation and save time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why I Built This Plugin&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While working on WordPress projects, I often needed a quick list of installed plugins and themes for documentation or troubleshooting.&lt;/p&gt;

&lt;p&gt;Since there wasn't an easy way to export that information, I decided to build a small tool that solves this problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;p&gt;If you'd like to try the plugin, you can install it from the WordPress plugin directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wordpress.org/plugins/site-extensions-snapshot/" rel="noopener noreferrer"&gt;https://wordpress.org/plugins/site-extensions-snapshot/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it, I’d love to hear feedback or suggestions from the developer community.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8g2m1wjx75bf055lilg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8g2m1wjx75bf055lilg3.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
Sometimes the simplest tools solve real developer problems.&lt;/p&gt;

&lt;p&gt;If you regularly manage WordPress websites, having a quick way to export installed plugins and themes can make documentation and audits much easier.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>wordpress</category>
      <category>opensource</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
