Before
The contact-form stylesheet loads on the home page and the slider script loads on a page with no slider.
After
The same file loads only where it is genuinely needed.
Every plugin you install loads its own CSS and JavaScript on every page, including the pages that never use it. This one starts by showing you the full list — handle, file, size and where it was seen — and then lets you switch any of them off by post type, page ID, template, archive, user role or device. Test Mode is on out of the box, so nothing you try reaches a visitor until you say so. The architecture is modular, and a module you switch off has its file left unread on the next request.
A WordPress site collects plugins, and every plugin loads its stylesheet and its script on every page — including the pages that never use them. The contact form loads on the home page, the slider loads on the contact page, and the page builder loads on posts written in the plain editor. Nobody decided that; it accumulated, one plugin at a time.
The hard part is not knowing that this is bad. The hard part is knowing what is actually loading. So the plugin starts there: the Asset_Scanner class hooks wp_enqueue_scripts and hands the admin screen the complete list for each page — handle, type, file path, file size and the page contexts it has been seen in. The scanner only records requests made by someone who can already manage the plugin, so a visitor's page view never writes a row.
A rule is one sentence: this handle does not load under these conditions. The conditions are post type, page ID, page template, archives, the front page, visitor role and device. Rules run from low priority to high and the last one that matches wins, which is what lets a broad off-everywhere rule be softened by a narrow keep-it-on-page-42 exception. A handle is never removed while something still in the queue depends on it, and it is dequeued rather than deregistered, so a later enqueue of the same handle cannot fail silently.
Test Mode is on by default. While it is on, rules apply to logged-in administrators only and a visitor sees the site exactly as before, so you can be as aggressive as you like and check the pages that matter before anything reaches anyone else. Switching Test Mode back on is also the fastest way out if a page does look wrong.
Everything is a module, and a module that is switched off is not merely hidden: the registry stores class names rather than instances, Module_Manager::is_active() answers from the stored option first, and the file is never read. tests/unit/isolation-test.php asserts this with a module that counts its own constructor calls, so the claim cannot quietly stop being true. The admin CSS is compiled with Tailwind under the hdmac- prefix, scoped to .hd-mac with preflight disabled, so nothing this plugin compiles can reach the rest of your dashboard.
Before
The contact-form stylesheet loads on the home page and the slider script loads on a page with no slider.
After
The same file loads only where it is genuinely needed.
Before
You know the site is heavy. You do not know which handle is responsible.
After
A full list, with the handle, the file, its size and the page contexts it was seen in.
Before
Removing one script means editing functions.php and hoping nothing else breaks.
After
You write the rule, check it with Test Mode on, then apply it to everyone.
Install the ZIP from Plugins → Add New → Upload Plugin. Two tables are created, and the conditional asset loader is switched on for you.
While logged in, open the home page, a post, a page and the contact page. Then press Refresh on the Assets tab and the list fills up.
Press Write a rule beside a row you know a page does not need, pick the condition and save. Test Mode is still on, so for now nobody but you sees the difference.
WordPress sites that have grown heavy over the years · web designers and agencies · WooCommerce shops · sites built with Elementor or Divi · news and magazine sites · teams that hand a dashboard over to a client · anyone who wants to see what is switched on before switching anything off.
Three free modules and two Pro ones, each behind its own switch. What you do not switch on does not run.
The registry stores class names, not instances, and the active check reads the stored option first, so a switched-off module's file is never read. tests/unit/isolation-test.php counts constructor calls to keep that claim honest.
Every stylesheet and script your site enqueues, with its handle, its type, the file, the file size and the page contexts it has been seen in. Recorded only from your own logged-in visits.
Post type, page ID, page template, archives, the front page, visitor role or device. Rules run in priority order and the last match wins, so a narrow exception can override a broad rule.
Rules apply to logged-in administrators only until you switch it off. Nothing you try can reach a visitor before you have looked at the pages that matter.
A handle is never dequeued while something still in the queue depends on it, and it is dequeued rather than deregistered, so a later enqueue of the same handle does not fail silently.
Default fonts, icon sets, dialog scripts, Divi's Google Fonts, emoji, wp-embed, jQuery Migrate and generator tags — each behind its own switch rather than one blunt toggle.
Elementor writes a CSS row per page into wp_postmeta and leaves it there. The count is shown before you delete anything, and Elementor rebuilds whatever it still needs on the next visit.
Move wp-login.php to an address of your own; anyone who goes to the old one is sent to the home page. A wp-config constant puts it back if you lock yourself out.
Decide which top-level menus each role sees, and rename the ones it keeps. The plugin's own menu is never hidden, because that would leave no way back.
Your logo on the login screen, your name in the admin footer, no WordPress logo in the toolbar and no generator tags in the page source.
Load Google Tag Manager, a chat widget or any other third-party tag only on the pages that need it, and only after the visitor's first interaction — or after eight seconds, so analytics stay honest.
Tailwind compiled with the hdmac- prefix, scoped to .hd-mac, preflight disabled. Nothing this plugin compiles can reach a WordPress element outside its own screens.
Seventeen filters and seven actions, each with a working example in docs/hooks.md — including registering a module of your own and teaching the rule engine a condition it does not know.
| Capability | Detail | Included |
|---|---|---|
| Per-page asset scan | Handle, type, file path, size and the page context it was seen in | |
| Rule by post type | Any post type registered on the site | |
| Rule by page ID | One specific page or post | |
| Rule by page template | Including the 404 page and search results | |
| Rule on archives | Category, tag and post-type archives | |
| Rule on the front page | For stylesheets only the landing page needs | |
| Rule by user role | A script that only means anything to a logged-in user | |
| Rule by device | Desktop or mobile | |
| Priority and evaluation order | The last rule that matches wins | |
| Dequeue only, never deregister | So a later enqueue of the same handle does not fail silently | |
| Dependency guard | Nothing still depended on in the queue is removed | |
| Test Mode | Rules apply to logged-in administrators only | |
| Administrator-only scanning | A visitor's page view never writes a row to the database | |
| Cached rule set | Read once per request from a transient, not from the database | |
| Observation log retention | Thirty days by default, changeable through a filter | |
| Emoji and wp-embed removal | Each behind its own switch | |
| jQuery Migrate removal | For a site with no legacy plugin left | |
| Elementor fonts, icons and dialogs | Three separate switches rather than one blunt one | |
| Divi Google Fonts | Without touching a theme file | |
| Generator tag removal | WordPress and page-builder versions out of the page source | |
| Generated CSS count and cleanup | The wp_postmeta row count is shown before anything is deleted | |
| Private login URL | With a way back through a wp-config constant | |
| Admin menus per role | Hiding and renaming, configured per role | |
| White label | Login screen logo, admin footer and no WordPress logo in the toolbar | |
| Conditional script runner | After the visitor's first interaction, or eight seconds at the latest | |
| Multisite | Supported, including sites added to the network after activation | |
| Complete Persian translation and an RTL panel | With a dedicated right-to-left stylesheet for the admin screens | |
| Data removed only on explicit consent | The delete-on-uninstall option is off by default |
| Item | Minimum |
|---|---|
| WordPress | 6.2 or newer (tested to 6.8) |
| PHP | 8.0 to 8.3 |
| Theme | Any theme; the plugin only loads CSS on its own admin screens |
| Multisite | Supported, including sites added to the network after activation |
| Page builder | Elementor or Divi only if you want the optimizer module; the rest of the plugin does not care |
A site that has been running for a few years and has collected plugins. If you built the site last week with two plugins, you can already name everything that loads and you do not need this. The plugin earns its place when nobody on the team can say with certainty which handle is responsible for the weight, and when that answer has to be found before anything is switched off. Agencies get the most out of it, because the same investigation repeats on every site they inherit.
In principle yes, and that is exactly what the safeguards are for. Test Mode is on by default and applies your rules to logged-in administrators only, so you see the effect before a visitor does. The loader refuses to remove a handle while something still in the queue depends on it, and it dequeues rather than deregisters, so a later enqueue of the same handle does not fail silently. If a page still looks wrong, switching Test Mode back on restores everything immediately, and you then delete the last rule.
There is no honest number to give you, because it depends entirely on what your site currently loads and how much of it a given page actually needs. What the plugin does is show you the file size beside each handle and let you remove specific ones, so you can measure the before and after yourself with whatever tool you already trust. Anyone quoting you a percentage without having seen your site is guessing.
Yes. Rules are evaluated while the page is generated, so the cached HTML is already the trimmed version. One caveat applies to every caching plugin and not just this one: if your cache does not keep a separate variant per device, do not use the device condition, because the first visitor's variant will be served to everybody.
One ZIP with every module in it: conditional asset loading with the full monitor, rule builder and test mode, the Elementor and Divi optimiser, dashboard customisation, white labelling and the conditional script runner. Nothing downloads separately and no module sits behind a check — each has its own switch on the Modules tab and you decide what runs.
Add define( 'HD_MAC_DISABLE_LOGIN_SLUG', true ); to wp-config.php and the original wp-login.php works again. A logged-in administrator can also always reach wp-login.php directly, which is the other way back in. The good habit is to test the new address in a second browser before closing the one you are logged in with.
The optimizer targets both by name, and builders rename their handles between releases. Rather than wait for a plugin update, the list of handles behind each toggle is filterable through hd_mac_builder_handles, so a handle that changed name can be added in a few lines. The conditional loader is unaffected either way, because it works from what your site actually enqueued.
Yes, both. The hd_mac_match_context filter teaches the rule engine a condition it does not know — an is_woocommerce() check, for example — and from then on it works everywhere a built-in condition does. The hd_mac_register_modules filter registers a class as a module, and it appears on the Modules tab with its own switch and its own settings. docs/hooks.md has a complete working example of each.
Nothing, unless you asked otherwise. Delete everything when the plugin is removed is off by default, so removing and reinstalling keeps your rules. Switch it on and uninstall takes away the two option keys, the two tables, the scheduled jobs and the transients — a short list, deliberately.
Bug fixes in the plugin, help with installation and setup, and answers to questions about using it. Deciding which handles your particular site can live without is your call rather than a support request — the panel exists so that call can be made from evidence. Customisation, new features and problems caused by other plugins or themes are agreed separately.