Not Malicious. Just Careless — What a PrestaShop Module Can Do That Uninstall Won't Undo
A paid PrestaShop module we found on a client's store was loading a decorative font from a third-party CDN on every single page — the homepage, every category, every product, the cart, the checkout. The font was used in exactly one place: the module's own settings screen, deep in the back office, where roughly one person in the company ever looked.
Here is what that carelessness actually cost, measured on the live homepage. The module added one new third-party domain, three extra requests per page (a stylesheet and two font files), and about 159 KB of weight that no visitor's browser needed. Because the stylesheet sat in the page <head>, it was render-blocking: every first-time visitor waited on a DNS lookup and a TLS handshake to a server the store owner had never heard of before the page could finish painting. On a throttled mobile connection the Largest Contentful Paint slipped from 2.4 to 2.9 seconds and the Lighthouse performance score dropped from the low 90s into the mid-80s. Not catastrophic. Just a tax, quietly levied on every page load, forever, for a font nobody outside the admin ever saw.
The blast radius was the whole store; the feature was one screen. That gap — between what a module touches and what it's actually for — is the entire subject of this article. We scoped the asset so the font only loads on the page that uses it, and the numbers went back to baseline. The fix took an afternoon. Finding out it was there took a browser and a habit.

That module wasn't malware. That's the point. Nobody was trying to hurt anyone. A developer wanted their settings page to look nice, reached for a font, and pasted in the CDN snippet the font's website gave them — then, out of the same copy-paste habit, also registered that same stylesheet on the global front-office header hook, even though only the settings page ever used it. Every decision was small and reasonable in isolation. The sum was a store-wide performance regression that no amount of "but it's a small module" would have predicted. Carelessness was enough. It usually is.
If you want to see how little it takes, here is the shape of the mistake — reconstructed, not the original vendor's code. The settings screen itself loads this font perfectly well in the back office; the damage came from a second, needless line that also registered the same stylesheet on the front-office header hook — the one that fires on every storefront page, where nothing uses it:
// Runs on EVERY page of the storefront — home, category, product, checkout
public function hookDisplayHeader($params)
{
// …yet nothing on the storefront uses this font — the settings screen loads it itself.
$this->context->controller->addCSS(
'https://3p-cdn.net/fancy-display/style.css' // third-party, render-blocking
);
}
Nothing here is malicious; it is a handful of lines that do exactly what they say. The trouble is that this hook has nothing to do with the settings screen the font is for. displayHeader renders on the storefront, so a font meant for one admin page is downloaded by every shopper — for nothing. The fix is to drop the storefront registration entirely and let the back office keep loading the font where it is actually used:
// The storefront never shows this font, so the front-office hook loads nothing.
public function hookDisplayHeader($params)
{
return;
}
// It loads only on this module's own settings screen in the back office —
// self-hosted, so no third-party domain and no extra DNS + TLS round-trip.
public function hookDisplayBackOfficeHeader($params)
{
if (Tools::getValue('configure') !== $this->name) {
return; // every other admin page pays nothing either
}
$this->context->controller->addCSS($this->_path.'views/css/fancy-display.css');
}
Same feature, same font — the storefront now loads nothing it doesn't use, and the store-wide tax disappears along with the third-party domain. That gap between the two versions is the whole difference between a module that stays in its lane and one that quietly charges every page for a feature almost nobody sees. Nobody had to be careless on purpose.
The problem is that modules pretend to be apps
Most merchants reason about modules the way they reason about phone apps. An app is sandboxed: it runs in its own walled garden, it can't reach into other apps, and if you don't like it you delete it and your phone is exactly as it was. That mental model is comfortable, and for PrestaShop it is completely wrong.
A PrestaShop module is not a sandboxed app. It is unsandboxed PHP running with the full privileges of your store. Once installed and enabled, it can hook into every page that renders, read and write any table in your database — orders, customers, prices, configuration — register overrides that quietly replace core behaviour, drop files into your theme, add columns and tables, schedule background jobs, and call out to external services on your behalf. It is not a guest in a walled garden. It is a member of staff with keys to the whole building, and you hired it by clicking "install".
A module isn't a feature you switch on. It's code you give your store's keys to. The question is never "is this feature nice" — it's "do I trust this code with everything my store can do."
None of that is a flaw in PrestaShop. That power is exactly why the platform is extensible enough to run a serious business on. But it means the safety model you're carrying in your head — the app model — is protecting you from nothing.
Put it concretely: one enabled module can reach all of this at once — it hooks into every page that renders, adds and alters database tables, drops override files over core classes, edits your theme, loads assets site-wide, schedules background jobs, reads and writes your orders and customer records, and calls out to external services. An app on your phone can do none of that to the phone. A PrestaShop module can do all of it to your store — which is why "just install it and see" is a bigger decision than it looks.

"It worked in my test shop" is where the trouble starts
The most expensive four words in module development are "it worked for me." A developer builds a module, installs it on a clean test shop, clicks around, sees it work, and ships. The trouble is that a clean test shop is nothing like a real store.
Your real store has a custom theme that overrides templates the module assumed. It may run multishop, where a module that only ever tested against shop 1 silently misbehaves on shop 2. It runs a specific PHP version that might be older, or newer, than the one the developer had. It has dozens of other modules already hooked onto the same pages, competing for the same output. It has real traffic, a cache layer, a live checkout taking real money, and — the big one — it will be upgraded someday, to a new PrestaShop version the module was never tested against. "It worked once, on one shop, on one version" is not a guarantee of anything. A module has to keep working across all of that, for years. That is a fundamentally harder promise than "it works," and most of the risk lives in the difference. It's also why module quality matters more than module quantity, and it's the hidden half of what actually makes PrestaShop slow.
What survives when you disable or uninstall
Here is the belief that does the most damage: "if it causes a problem, I'll just uninstall it and everything goes back to normal." Sometimes that's true. Often it isn't, and the difference is worth understanding precisely — because the confident version of this claim is how stores end up quietly broken.
Disabling a module stops its runtime hooks from firing. That genuinely helps: the module stops injecting its assets and running its code on each page, so a live performance or display problem usually eases immediately. Disabling is the right first move in an emergency. But disabling does not reach back and undo anything the module already did.
Uninstalling goes further and runs the module's own cleanup routine — and that routine only removes what its author bothered to write code to remove. What does not reliably come back on its own:
- Database and configuration changes. New tables, new columns, and configuration rows may be left behind. Some modules deliberately keep your data on uninstall so you don't lose settings on a reinstall — a reasonable choice, but only if they tell you.
- Order and customer data. Anything written into your orders, invoices, or customer records during normal operation is your live business data. Uninstalling the module does not, and should not, delete it — but it may leave it in a shape only that module understood.
- Overrides and theme edits. Files dropped into
/overrideor into your theme can outlive the module. A stale override left in place after uninstall is a classic source of "my store broke and nothing I disabled fixed it." - External integrations. Data the module pushed to a third-party service — a feed, a CRM, an analytics or marketing platform — is already gone from your store's control. Uninstalling changes nothing on the other end.
So the accurate sentence is: disabling stops the bleeding, but uninstall does not undo the database writes, configuration changes, order and customer data, overrides, theme edits, or external calls a module already made. "I'll just uninstall it" is a plan for the feature. It is not a plan for the footprint.
It helps to picture the three states side by side. Disabling stops the module running. Uninstalling runs its cleanup routine. But a whole column of things — data written to your orders and customers, configuration, overrides, theme edits, and anything already sent to an outside service — survives both, unless the module's author wrote code to reverse them and you confirmed it did. That last column is the one that surprises people at the worst possible moment.

We've shipped this mistake too
It would be dishonest to write all this as if it only happens to other people. It doesn't. We build and sell PrestaShop modules for a living, and we have made the same class of mistake.
On one of our own modules, a front-office script that a single feature needed got registered on a hook that fires on every page, rather than only on the controllers where the feature actually appears. Functionally everything worked, so it passed review and shipped. It was a performance audit months later — the same kind of audit in this article — that caught a small script loading site-wide for no reason on pages that never used it. The cost wasn't dramatic: a few kilobytes and a little parse time on every page, across the catalog, until we fixed it. But it was real, it was ours, and "it worked" had waved it straight through.
What changed wasn't a lecture about being more careful. We turned it into a question the review has to answer for every asset and every hook: does this really need to be global, or does it belong on the pages that use it? A scar becomes a rule, and the rule catches the next one. That's the honest version of quality — not "we never make mistakes," but "we built the thing that catches them before you do." It's the same discipline behind getting a real store with 80+ modules to a 99 PageSpeed score: not fewer modules, but modules that stay in their lane — which is the whole design brief of our Performance Revolution suite.
Then isn't AI the villain here?
You might expect a module vendor to end this by blaming AI — cheap AI-generated code flooding the market with slop. We're not going to, because it would be both unfair and hypocritical. We use AI-assisted engineering ourselves, heavily. That is precisely why we don't judge a module by whether an AI touched it.
AI is an accelerator. It makes good engineering faster and it makes careless engineering faster, and it has no opinion about which one you're doing. The font-on-every-page mistake predates the current AI wave by a decade; a rushed human wrote plenty of those. What separates a module you can trust from one you can't has never been the author's tooling. It's governance: is there a named owner, is the code reviewed, is it tested, is it kept compatible as PrestaShop moves, can its changes be reversed, does it stay inside its performance budget, will someone maintain it next year. Those questions are exactly as valid whether the code was typed by a person, generated by a model, or — as is now normal — written by a person and a model together. Ownership, review, and testing are the subject. Authorship is a distraction.
Judge us by the same test
We sell modules too, so it would be easy to read all of this as "buy from us instead." Don't take our word for it — that's the whole spirit of the thing. The fair move is to hold every vendor, us included, to the same standard, and the good news is that most of the important checks need no PHP literacy at all. You can run them from a browser on a staging copy of your store: install the module, open your browser's Network tab on your homepage (not the module's page), and count what changed.
We wrote up the full procedure as a plain-English reference — how to vet a PrestaShop module before installing it on a live store — and we mean it as a tool to use against our catalog as readily as anyone's. If you'd rather have someone run it for you, that's exactly what our performance and security audits do, and our free security scan is a no-cost place to start. Turn the checklist on us. We'd rather you did.
Common questions
Does uninstalling a module delete everything it created?
No — not reliably. Uninstalling runs the module's own cleanup routine, which only removes what its author wrote code to remove. Database tables, configuration rows, files in /override, theme edits, and any data already sent to an external service can all survive. It should also never delete your orders or customer records, because that is your live business data. Assume uninstall tidies up the feature, not the whole footprint.
Can one module really slow down my entire store?
Yes. When a module loads assets or runs code on a global hook — one that fires on every page — that cost is paid by every page, including your homepage and checkout, even pages the feature never appears on. That is exactly how a font meant for a single admin screen ended up taxing a whole storefront.
Is a module unsafe just because AI helped write it?
No. AI is an accelerator, not a verdict. What makes a module trustworthy is ownership, review, testing, compatibility, reversibility and performance discipline — and those apply whether the code was written by a person, a model, or both. Judge the governance, not the authorship.
How do I actually check a module before I install it?
On a staging copy, install it and watch what changes: new third-party requests on your homepage's Network tab, new database tables, whether checkout still works, and whether it uninstalls cleanly. We wrote the full step-by-step as how to vet a PrestaShop module before installing it.
The best modules are boring
After all the war stories, the conclusion is almost anticlimactic, and that's the point. A good module is boring. It does the one thing it advertised. Its assets load only where they're used. It touches only the data it needs, tells you what it keeps, and cleans up what it can. It survives your next PrestaShop upgrade without drama. And the truest sign of its quality is that, six months after installing it, you have completely forgotten it's there.
Excitement, in a module, is almost always the sound of something going wrong on a page it should never have touched. Boring is the goal. Boring is what "it just works" actually looks like from the inside — and it's worth checking for, on purpose, before a stranger with the keys to your whole store moves in.
Comments
No comments yet. Be the first!
Be the first to ask a question or share useful feedback.
Leave a comment
Share a question, an installation detail, or feedback that could help another reader.