Legacy sites
Magento 1.9 in 2026: a war-story from an Antwerp exporter
We inherited a Magento 1.9 store in 2026, six years past end-of-life, with a patched Stripe module, a broken cron, and a tax table last touched in 2019. Here is what we found.

The handover happened over coffee at a freight forwarder's office in Antwerp, between the loading bay and a stack of customs paperwork. The owner of a 16-person specialty foods exporter slid a Post-it across the table with one SSH password on it. "The webshop is yours," he said. "Our last developer left in 2019."
We logged in that evening from a kitchen table in Roeselare. What we found was a Magento 1.9.4.1 install, last touched in late 2020, running on PHP 7.0 behind an nginx that still trusted a Let's Encrypt cert renewed by a script nobody could find. Stripe was hand-patched. Cron had been dead for so long the newsletter queue had 11,400 unsent rows. And the tax table, which controlled VAT on every order leaving the warehouse, had a last-modified timestamp of 14 March 2019.
This is what that week looked like.
The state of Magento 1 in 2026
Magento 1 reached end of life on 30 June 2020. Adobe was clear about it: no further security patches, no support, migrate to Magento 2 or somewhere else. They announced the deadline in 2018 and it did not move.
What that meant, six years on, was an install carrying a stack of unpatched CVEs against the core, including the SQL injection chains that fed the 2020 MageCart skimming wave. The store was still up because the host had a WAF rule blocking the most obvious payloads, and because the previous developer had patched a handful of the worst issues by hand. Patched. By hand. In app/code/core/Mage/. We will come back to that.
The catalog: 1,840 SKUs of charcuterie, pralines, biscuits, and Trappist beer. Annual GMV around €2.1M, of which roughly 64% was cross-border B2C inside the EU and 28% was B2B intra-community. Around €130k a month flowed through the Stripe integration the previous developer had wired in.
An unpatched Magento 1 in 2026 is not "running fine". It is running until the day a scanner from a botnet finds it. Treat the silence as luck, not safety.
The patched Stripe module
The Stripe integration was a fork of an old community module from 2017. Git history ended in November 2018. After that, every change was a direct edit committed nowhere, only the file mtimes told us anything had happened.
The patches were not malicious. They were heroic, in the way a developer being asked to keep a dying system alive is heroic. The previous engineer had bumped the pinned Stripe PHP SDK to keep TLS requirements working after Stripe raised the floor in 2018. He had hand-edited the request body to add payment_method_types[]=card because the old Charges API call started returning warnings. And he had wrapped the SCA authentication path in a quiet fallback:
// app/code/community/[Vendor]/Stripe/Model/Payment.php
try {
$charge = \Stripe\Charge::create([...]);
} catch (\Stripe\Error\Card $e) {
if (strpos($e->getMessage(), 'authentication_required') !== false) {
// FALLBACK: redirect to a Stripe-hosted page
// works on some banks, silently drops the cart on others
$this->_redirectFallback($order);
return;
}
throw $e;
}
That last block was costing real money. When we joined the orders table with Stripe's dashboard, 7.4% of attempted EU card payments since mid-2021 had failed at the 3-D Secure step and the cart had not recovered. Stripe's own migration guide to PaymentIntents exists for a reason: SCA under PSD2 stopped being optional in 2021.
Net effect on the books: roughly €92k of orders had been quietly walking away each year. The owner thought conversion was just "a bit lower lately".
The cron that was not
Magento 1 needs cron for almost everything that is not a page render. Reindexing, catalog price rules, newsletter sending, abandoned cart emails, sitemap generation, search term aggregation. The store's cron.sh had been silent since early 2023.
The cause was banal. The hosting provider had migrated the box from CentOS 7 to AlmaLinux 9 during a 2023 maintenance window. The crontab was preserved. The PHP binary at /usr/bin/php was now PHP 8.1, not PHP 7.0. The shop ran on a separate PHP-FPM pool pinned to 7.0 via a path that cron did not know about. Every cron run fataled on a strict type warning in a vendor library and exited 255. Nothing logged, because the cron line redirected stderr to a path that had been deleted in the same migration.
*/5 * * * * /usr/bin/php /var/www/shop/cron.php >> /var/log/magento.log 2>&1
# /usr/bin/php is now 8.1, not 7.0
# /var/log/magento.log was deleted in the OS migration
# cron.php throws on PHP 8.1 and exits 255
# no errors visible anywhere
One line of crontab. Three years of silent failure. Newsletters, abandoned carts, reindex, all dead. The newsletter queue had grown to 11,400 rows. When we revived it on a staging copy and let it flush, it sent four years of "welcome to our newsletter" emails to people who had since unsubscribed in their heads. We did not let that go to production.
A tax table frozen in 2019
This was the one that made the owner sit down.
Belgian specialty foods sit across three VAT rates: 21% for most chocolate and confectionery, 12% for some prepared foods, 6% for unprocessed groceries and most foodstuffs. The local core_config_data still had the right Belgian rates. The cross-border rules did not.
Until 1 July 2021, EU distance selling worked on a per-country threshold, typically €35,000 or €100,000. Below it, you charged Belgian VAT. Above it, you registered in the destination country and charged theirs. The 2019 tax tables were correct for that world.
The OSS scheme replaced it. From July 2021, the threshold dropped to €10,000 across the entire EU, and above it you charge the customer's local VAT rate and remit through a single quarterly One Stop Shop return. The European Commission documents the change in plain language. The shop had crossed €10k of cross-border B2C in week three of January 2022 and not noticed.
Four and a half years of orders to France, Germany, the Netherlands, and Italy had been invoiced at Belgian VAT rates when they should have been at French, German, Dutch, and Italian rates. Some over-collected (Italy at 22% against Belgian 21% on the wrong line). Some under-collected (Germany at 7% reduced against Belgian 6%). When our accountant ran the arithmetic, it came to a net under-collection of around €38,000 against the Belgian return, plus an unfiled OSS liability.
A tax engine on a legacy shop is not a settings page. It is a regulatory clock that ticks whether anyone is reading the dial or not.
The migration call
The owner asked the question every owner asks at this point: "Can you just fix it?"
The honest answer was no, not for long. We could keep the patient breathing for a quarter. We could not keep it alive for a year without either rebuilding it or pouring money into hand-patching a six-year-dead platform. Migrating Magento 1 to Magento 2 is not a migration in any meaningful sense; the database is similar, the application is a different product. The team had also lost interest in Magento as a brand.
We laid out three paths in a one-page memo:
- Triage now, rebuild on Shopify within four months. Lowest engineering cost, highest platform fees, easiest hand-off to the in-house marketing lead.
- Triage now, rebuild on a headless Next.js plus Medusa stack within six months. Higher engineering cost, full control, no platform fees beyond hosting.
- Stay on Magento 1, keep patching. Cheaper this quarter. Same conversation again in twelve months with worse compounding.
They picked option one. Specialty foods at €2.1M GMV does not need a custom storefront. It needs to take money correctly and ship the box on time.
The first thirty days
Before any rebuild work, we did the bleeding-stopping pass. None of this is glamorous. All of it had to happen first.
Day 1 to 3. Took a full database dump and a tarball of app/code, skin, media, and var. Stored offsite, encrypted. Confirmed we could restore both into a clean container locally. If the live box died in week two, this was the rollback.
Day 3 to 7. Stripe. We did not try to migrate the integration. We dropped a Stripe Checkout redirect in front of it for new orders and let the existing PaymentIntents on file keep working for refunds and recurring authorisations. SCA failures dropped under 1% inside a week.
Day 5 to 10. Cron. Rewrote the crontab to call the exact PHP 7.0 binary path that the FPM pool used. Pointed logs at a path that existed. Watched the indexer catch up over an afternoon. Killed the four-year newsletter backlog without sending it.
Day 7 to 21. Tax. Disabled the broken cross-border calculation entirely and routed all EU B2C orders through a single rule that called out to an external VAT service before order placement. Filed back-VAT corrections with the accountant. Registered for OSS. The €38k under-collection became a payment plan, not a fine.
Day 21 to 30. Started the Shopify rebuild in parallel: theme scaffolding, catalog export, customer import, Belgian shipping rules. Live cutover planned for week 14.
What we tell every owner of a legacy shop
You do not need to panic. You need to look. Most owners we meet have not opened their Magento, Drupal, or WordPress admin in eighteen months. They are not negligent. They are running a business. The shop is in their peripheral vision, working until it isn't.
The five-minute audit is free and you can do it tonight:
- Open the admin. Note the platform version. Look up its EOL date.
- Open the crontab. Find the line that runs your shop's cron. Run that exact command by hand. Read the output.
- Open one order from yesterday and one from three years ago. Look at the VAT line. Compare it to your accountant's expectation for that country.
- Open the payment integration. Look at the file modification dates. Look at the vendor SDK version.
- Open your hosting provider's status archive. Find the last OS or PHP upgrade window. Ask yourself what nobody tested afterward.
If any of those five make you uncomfortable, you have found the right place to start.
When we ran this engagement for the Antwerp exporter, the thing we kept coming back to was that nothing in their stack had failed loudly. The Stripe module did not throw. The cron did not page. The tax table did not warn. Silent failure is the default failure mode of legacy commerce, and the only defence is to go look. We do this kind of legacy migration work the same way every time: stop the bleeding before the rebuild, and rebuild on a platform that fails loudly when it fails.
The smallest thing you can do today: SSH into your shop's server, run php cron.php by hand, and read every line it prints.
Key takeaway
Legacy commerce fails silently. The Stripe module does not throw, the cron does not page, and the tax table does not warn until your accountant does.
FAQ
Is Magento 1.9 still safe to run in 2026?
No. Magento 1 reached end of life on 30 June 2020. There have been no official security patches since, and the unpatched CVEs against the core make any public install a target for opportunistic scanners.
What is the EU OSS scheme and when did it start?
One Stop Shop took effect on 1 July 2021. Above €10,000 of cross-border EU B2C sales a year, you charge the customer's local VAT rate and remit via a single quarterly OSS return instead of registering per country.
Should you migrate Magento 1 to Magento 2?
Not automatically. Magento 2 is a different product, not a version bump. If you do not need deep customisation, Shopify or a headless stack on Medusa is often a faster and cheaper landing.
Why did the patched Stripe module quietly lose money?
It caught SCA authentication errors and fell back to a redirect that worked for some EU banks but not others. Failed 3-D Secure attempts looked like normal abandoned carts in the analytics rather than payment errors.