<?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: Laurentius Judhianto</title>
    <description>The latest articles on DEV Community by Laurentius Judhianto (@laurnts).</description>
    <link>https://dev.to/laurnts</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%2F3962808%2F0fc6e741-fc75-49d7-96b3-c32ba7a72e27.jpg</url>
      <title>DEV Community: Laurentius Judhianto</title>
      <link>https://dev.to/laurnts</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/laurnts"/>
    <language>en</language>
    <item>
      <title>Meet Mago: The Magento Admin Assistant Where Security Is Not a Second-Class Citizen</title>
      <dc:creator>Laurentius Judhianto</dc:creator>
      <pubDate>Tue, 10 Nov 2026 10:45:44 +0000</pubDate>
      <link>https://dev.to/laurnts/meet-mago-the-magento-admin-assistant-where-security-is-not-a-second-class-citizen-39j7</link>
      <guid>https://dev.to/laurnts/meet-mago-the-magento-admin-assistant-where-security-is-not-a-second-class-citizen-39j7</guid>
      <description>&lt;p&gt;&lt;strong&gt;Mago is the open-source admin assistant coming with Mage-OS 4. Who builds it, why it beats the first attempt, the MCP addon I built at the Zwolle hackathon, and what it really costs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is a new admin assistant for Magento and Mage-OS called Mago, and it is the first one I would actually put in front of a merchant. You can read about it on &lt;a href="https://askmago.com" rel="noopener noreferrer"&gt;askmago.com&lt;/a&gt;. Before I tell you what I like about it, what it costs, and what I built on top of it, let's start with the people behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who builds Mago
&lt;/h2&gt;

&lt;p&gt;Mago is built in the open by the Dutch Magento community, together with Mage-OS Nederland. The partners named on the site are Made by Mouses, Magmodules, Linted, Vendic and Control Alt Delete.&lt;/p&gt;

&lt;p&gt;Looking at the &lt;a href="https://github.com/mago-assistant/mago" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;, most of the work comes from Michiel Gerritsen (Control Alt Delete), followed by Haimanti Dekker, Marvin Besselsen (Magmodules), Mitchel van Kleef and Lars de Weert (Made by Mouses), Peter Stuifzand, Sean van Zuidam and Henk Valk, with more contributors joining every week. Mago Core is free and MIT-licensed. No seats, no subscription, no paid tier.&lt;/p&gt;

&lt;p&gt;And behind all of them is Mage-OS, the community-run distribution of Magento Open Source. If you want to know what Mage-OS is and why we moved all our customers to it, I wrote about it in &lt;a href="https://www.storeframe.io/blog/why-we-migrated-all-customers-to-mageos" rel="noopener noreferrer"&gt;Why We Migrated All Our Customers to Mage-OS&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before Mago, there was the Mage-OS Lab admin assistant
&lt;/h2&gt;

&lt;p&gt;Credit where it is due. Mago is not the first attempt. Ryan Sun built the &lt;a href="https://github.com/mage-os-lab/module-admin-assistant" rel="noopener noreferrer"&gt;Mage-OS Lab admin assistant&lt;/a&gt; back in early 2025: a chatbot in the Magento admin that combined an LLM with Magento domain knowledge. What I found genuinely original was that it did RAG in PHP. You ran &lt;code&gt;bin/magento assistant:train&lt;/code&gt;, it built document embeddings, and the chatbot answered with that knowledge behind it. In a world where every AI project starts with a Python service next to the shop, doing it inside Magento itself was a bold choice.&lt;/p&gt;

&lt;p&gt;The downside was that it was never really polished. It often got answers wrong, and sometimes even a normal installation failed. We played around with it, but eventually gave up. It simply wasn't good enough compared to a standard Claude Code terminal session on the server.&lt;/p&gt;

&lt;p&gt;Mago feels like the version of that idea that grew up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters to StoreFrame
&lt;/h2&gt;

&lt;p&gt;At StoreFrame our mission is to bring operations and Magento together through AI and infrastructure. I wrote about this in &lt;a href="https://www.storeframe.io/blog/agentic-operations-not-agentic-commerce" rel="noopener noreferrer"&gt;Agentic Operations, Not Agentic Commerce&lt;/a&gt;: everyone talks about AI that shops for the customer, but the real pain sits on the operational side, with the people who run the store every day.&lt;/p&gt;

&lt;p&gt;Mago validates that. A whole group of Dutch agencies sat down and built an assistant for the admin, not for the storefront. That tells me the operational need is much larger than the commerce need.&lt;/p&gt;

&lt;p&gt;It is not a replacement for what we built in StoreFrame. We work at the infrastructure level: servers, deployments, security, monitoring. Mago works inside the admin, with the merchant. It is a great addition, and the two fit together well.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes Mago stand out
&lt;/h2&gt;

&lt;p&gt;There are a lot of AI chat widgets for Magento. These are the things that make Mago different.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The UI.&lt;/strong&gt; It is simply good. Answers stream in while Mago works, results come back as proper widgets instead of walls of text, and slash commands like &lt;code&gt;/cache flush&lt;/code&gt; run directly without an AI call. As someone with an interaction design background, this is the part that made me smile first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The classifier.&lt;/strong&gt; Every tool has to classify every field it returns. Names, emails, phone numbers and addresses are stripped before anything reaches the AI provider, order and customer numbers become placeholders like &lt;code&gt;[order_1]&lt;/code&gt;, and any field that is not explicitly marked safe never leaves the store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The safety guardrails.&lt;/strong&gt; Reads run immediately, every write asks for confirmation. Irreversible actions, like cancelling an order or creating a credit memo, show exactly what will happen first and need an extra acknowledgement. Some settings are not offered at all, such as base URLs, the SMTP host and the store email addresses, because one wrong value there hands over the whole store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ACL.&lt;/strong&gt; Everything follows Magento's own ACL. Read and write are separate permissions, and skills can be allowed per user. Each admin sees only what their role allows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security is not a second-class citizen in Mago. It is designed in from the start, and you can tell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging and audit, done properly
&lt;/h2&gt;

&lt;p&gt;What makes it even more special is the logging. Conversations, tool calls and the queries behind them are recorded, so you can trace back exactly what the LLM did: which tool it called, with what input, and what came back. Together with the detectors and the answer feedback, it is audit-ready out of the box.&lt;/p&gt;

&lt;p&gt;When you let an AI touch a production store, this is what you need. It is simply impressive, and honestly, this is what high-end Magento modules look like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Addons: extend before you modify
&lt;/h2&gt;

&lt;p&gt;Mago has a plugin API, so other modules can add their own skills without touching the core. There are already addons contributed for checking a Dutch company in the &lt;a href="https://github.com/mago-assistant/magento2-kvk" rel="noopener noreferrer"&gt;KvK register&lt;/a&gt; and validating an EU VAT number against &lt;a href="https://github.com/mago-assistant/magento2-vies" rel="noopener noreferrer"&gt;VIES&lt;/a&gt;, plus ones for reporting issues to GitHub and reading Hypernode server load.&lt;/p&gt;

&lt;p&gt;This is how it is meant to grow: build an addon first, and only change the core when you really have to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shipping with Mage-OS 4
&lt;/h2&gt;

&lt;p&gt;Mago ships with Mage-OS 4.0.0, released on 30 October. It also runs on Magento 2.4.9 and Mage-OS 3.0, but Mage-OS 4 is where it comes bundled.&lt;/p&gt;

&lt;p&gt;Mage-OS 4 is one of the biggest feature releases so far, and it goes deeper into AI. It is built on the same Magento Open Source 2.4.9 base as 3.6, with no change in PHP support, so it is a drop-in upgrade from 3.6.x, and most new features stay off until you turn them on. Next to Mago it brings a shared AI layer that works with whichever LLM provider you choose, llms.txt files and crawler controls for AI discoverability, passwordless login with passkeys, native product feeds for Google, Meta, Pinterest, TikTok and Microsoft, and an overhaul of Multi-Source Inventory that closes a gap where two orders could oversell the same stock. Under the hood, setup:di:compile is faster and WebP and AVIF images are supported natively. The full list is in the &lt;a href="https://mage-os.org/releases/2026-10-30-mage-os-4-0-0-release/" rel="noopener noreferrer"&gt;release announcement&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Damien Retzinger from Graycore, the creator of Daffodil, shared the video of the Mage-OS meeting where Mago is demoed. It is worth watching to see it in action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=O5SWmzbkZ3c" rel="noopener noreferrer"&gt;▶ Mago demo at the Mage-OS meeting&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The MCP addon: made in Zwolle, perfected in Amsterdam
&lt;/h2&gt;

&lt;p&gt;On 24 September I joined the Mage-OS hackathon in Zwolle, hosted by Made by Mouses, where we sat together and built on Mago. I mentioned it, with photos, in &lt;a href="https://www.storeframe.io/blog/why-we-migrated-all-customers-to-mageos" rel="noopener noreferrer"&gt;the Mage-OS post&lt;/a&gt;. My contribution was one of the MCP addons.&lt;/p&gt;

&lt;p&gt;MCP is the protocol AI tools use to talk to other systems. The addon turns every tool from an MCP server into a Mago skill:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local MCP servers over stdio&lt;/strong&gt; are detected automatically, from installed Composer packages and from the &lt;code&gt;.mcp.json&lt;/code&gt; file in the Magento root.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote MCP servers&lt;/strong&gt;, like Context7 for up-to-date documentation, can be added through the admin portal, with no token, a shared bearer token, or OAuth per admin user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing is enabled by itself.&lt;/strong&gt; An admin turns each server on, and every tool is classified as read or write by its name. Anything unrecognised is treated as a write, so it goes through Mago's confirmation card.&lt;/li&gt;
&lt;li&gt;Output stays private by default. Results go through Mago's privacy filter unless an admin switches it off for a server whose answers contain no personal data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, on 4 November, at the hackathon in Amsterdam at the Bold Commerce office, I went back to it and tightened its security and permissions, to make it much harder to break a webshop with it. Every MCP server now starts read-only, so only its read tools are exposed. A write is only offered after an admin unticks read-only and then ticks that one tool. Tools that run code, SQL or shell commands, or that handle credentials, are withheld completely until someone deliberately enables them. So when Bricklayer or n98-magerun is installed, Mago gets their lookups and nothing that can change or run anything.&lt;/p&gt;

&lt;p&gt;The output follows the standard set by Mago's core module too. The privacy filter is on for every new server, so results pass through Mago's own scrub, which hides emails, phone numbers and values Mago already knows before anything reaches the AI provider. Passwords, tokens and encrypted config values are masked in every case. And an MCP tool can only read customers, orders or configuration for an admin whose Magento role is allowed to open those screens.&lt;/p&gt;

&lt;p&gt;If you would like to test it, this is the branch: &lt;a href="https://github.com/mago-assistant/magento2-mcp/tree/feature/mcp-http-and-stdio" rel="noopener noreferrer"&gt;mago-assistant/magento2-mcp, feature/mcp-http-and-stdio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The module is still under development, hence the branch. I have spent quite some time making sure it respects the security guardrails and the system from Mago's core module, but a review from a senior backend developer would make it even better. Any developer is welcome to review it and make changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  A disclaimer: power comes with great responsibility
&lt;/h2&gt;

&lt;p&gt;With the release of Mage-OS 4, installing the MCP module means Bricklayer MCP, and possibly n98-magerun, will be installed in your vendor folder too. That makes it extremely powerful. It can do many good things, and it can do many bad things too.&lt;/p&gt;

&lt;p&gt;I deliberately tried a few techniques to crash the module or shut down the webshop. Somehow the admin assistant was good enough to warn me, prevent it, and refuse certain actions, which makes it even better. But don't take my word for it as a guarantee. Try it on staging, and at your own risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  The downside: every chat costs money
&lt;/h2&gt;

&lt;p&gt;It is not fair to only talk about the positives. Mago calls an LLM provider's API, so every chat costs something. A simple question might cost from 0.3 cents, up to a euro for a long, complex one. If you run long chats and complex questions several times a day, it can become unsustainable.&lt;/p&gt;

&lt;p&gt;To be fair, you pay your provider directly at list price with no markup, and you can set a monthly spending limit. But while developing I am using an Anthropic API key, and it is definitely not cheap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Subscription: using your Claude plan instead of an API key
&lt;/h2&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/laurnts/magento2-claude-subscription" rel="noopener noreferrer"&gt;magento2-claude-subscription&lt;/a&gt;. It is for when you would rather use your Claude subscription than an API key, especially as a developer building Mago features, addons and everything around it, where you chat with the assistant all day.&lt;/p&gt;

&lt;p&gt;It plugs into Mage-OS AI Base as just another AI provider, so in Mago you simply pick it as the AI service. Next to the shop runs a small Node host: you log in once with &lt;code&gt;claude auth login&lt;/code&gt; on that machine and start it with &lt;code&gt;bin/claude-subscription start&lt;/code&gt;. Magento sends every chat turn to that host as a normal chat completion request, and the host runs &lt;code&gt;claude --print&lt;/code&gt; for it with your subscription's login. Claude's own tools and MCP servers are switched off for that run, and it starts in an empty folder, so Claude only answers in text. Mago's tool calls are passed through as tags, and Mago still asks an admin to confirm every write.&lt;/p&gt;

&lt;p&gt;The downside is that the host is a persistent process you have to start and log in to yourself, and every admin who can open Mago shares that one Claude account. It is perfect for development and testing, but not suited for production. Check your Claude plan's terms too. Again, use it at your own risk, you have been warned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Mago is the Magento admin assistant I wish existed when we tried the first one. It has a great UI, it treats privacy and security as first-class, and it is open and easy to extend. The cost is real, so watch it. But if you run a Magento or Mage-OS store, keep an eye on Mage-OS 4, try Mago on staging, and if you can review PHP, have a look at the MCP branch.&lt;/p&gt;

</description>
      <category>magento</category>
      <category>mageos</category>
      <category>ai</category>
      <category>agentic</category>
    </item>
    <item>
      <title>What StoreFrame is to Me and It's Personal.</title>
      <dc:creator>Laurentius Judhianto</dc:creator>
      <pubDate>Thu, 29 Oct 2026 10:57:46 +0000</pubDate>
      <link>https://dev.to/laurnts/what-storeframe-is-to-me-and-its-personal-2jh6</link>
      <guid>https://dev.to/laurnts/what-storeframe-is-to-me-and-its-personal-2jh6</guid>
      <description>&lt;p&gt;&lt;strong&gt;So you know the person behind it. Not a sales pitch, just what StoreFrame is to me: a home for my team, my notes as working code, and the impossible thing I'm glad I tried to build.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Heads up: this isn't a product page and it isn't a sales pitch. It's me talking about the thing I've spent years building, with the emotions left in, including a swear word or two. If you're looking for features and pricing, the rest of the site has you covered. This one is just human.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  It's my digital home
&lt;/h2&gt;

&lt;p&gt;StoreFrame, for me, &lt;strong&gt;is home&lt;/strong&gt;. It's where I'm connected with my remote team, my customers, my partners and my own thoughts.&lt;/p&gt;

&lt;p&gt;Every morning, instead of opening Jira tickets, emails and everyone else's systems, we open our own and work there. We know if it's running, we know if it's ill, because we use it ourself every day. I can share my thoughts with my team, and they can share theirs with me. I can pick up their work and see where they got stuck, and they can review my work and carry it on. I can asked AI how my team write their prompts, how they structure their code and same goes with them checking what I am doing. There is no need to wait for the end for a sprint review, I can simply asked AI to rewrite the entire chat history person to person. Same with my team and same with my customers (if they asked too).&lt;/p&gt;

&lt;p&gt;Many times I have continued my team work where they left of, sames goes with mine. If I am tired waiting for Claude to ultrathink, I can simply hand it off to my team. They can continue the chat, discover the intent, check the plan, double check it with their own expertise. This is development around the clock across remote teams.&lt;/p&gt;

&lt;p&gt;Its value goes beyond hosting and infrastructure. For us it isn't a nice-to-have. It's something we need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick a title, any title
&lt;/h2&gt;

&lt;p&gt;I can call myself whatever I want: a designer, a DevOps, a DevSecOps, a hacker, a CTO, or nobody at all. It doesn't matter. I never liked programming in the first place, yet I like connecting things together. As an interaction designer, it's us who translate human language into system language. Thats the job. The programming is what comes with it.&lt;/p&gt;

&lt;p&gt;I chose platform engineering, and not because I write the perfect CI/CD pipeline or a best-in-class Ansible workflow. No. It's because platform engineering, at its core, is about &lt;strong&gt;building a platform that connects the people in an organization&lt;/strong&gt;. It smooths the internal process, helps my team and my customers get the most out of us, and keeps everybody aligned. I think that's the real value.&lt;/p&gt;

&lt;h2&gt;
  
  
  My notes, but running
&lt;/h2&gt;

&lt;p&gt;I started as a developer through trial and error, copy-pasting from Stack Overflow. For years I kept lots of code snippets in my notes for doing this and that, until that got impractical.&lt;/p&gt;

&lt;p&gt;So instead of piling up notes, GitHub stars and bookmarks, I put it all into a system as working code. A bookmark is fire-and-forget. Building the thing, doing the actual work, is what makes me remember it, and see how it really works and fits with everything else.&lt;/p&gt;

&lt;p&gt;With AI today, it keeps growing, fast. At least the system also keeps track of all of it for me. Maintaining it is another drama, of course. It's inevitable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honestly, Magento bores me &lt;del&gt;sometimes&lt;/del&gt;
&lt;/h2&gt;

&lt;p&gt;We deal with Magento every day. Even after almost two decades there's still something to do, to fix and to learn every day, no matter who you are. But at the end of the day, it's still Magento. And sometimes, as a human, I'm very bored of it.&lt;/p&gt;

&lt;p&gt;So StoreFrame is the perfect side project. It's not me improving &lt;em&gt;in&lt;/em&gt; Magento. It's me improving how the Magento work itself gets done.&lt;/p&gt;

&lt;p&gt;And just in case one day there's no Magento, my whole career doesn't go in the bin. I can still show my design skills, my engineering principles, my architecture, and how I solved the boring parts. It's my portfolio, not bound to any certificates and client's demands. &lt;strong&gt;Freedom to express&lt;/strong&gt;. The language, the framework and everything else can go in the bin tomorrow. The knowledge and experience stay, even beyond StoreFrame itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a control panel is seriously f'ing tough
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Seriously.&lt;/strong&gt; Ask anyone in the industry, or your own team. If it were easy, everyone would have one. The fact that they don't tells you a lot. You have no idea how much money has gone into building version after version of it over the years.&lt;/p&gt;

&lt;p&gt;I've been through thousands of VM provisionings on Hetzner, from zero to just production grade Magento. Literally thousands, to get the variability and the dependencies structured. And it had to adapt to whatever is already there, and to whatever comes in next.&lt;/p&gt;

&lt;p&gt;Yet the most bullsh*t part is that you can't release** a control panel half-baked**. &lt;strong&gt;This is a serious one&lt;/strong&gt;. When is it enough to release, and where does it end? That was hard to define. I can't ship StoreFrame as just a store-creation wizard alone. And catching up from zero with systems that have been around for decades is absolute insanity, especially doing it all alone, bootstrap.&lt;/p&gt;

&lt;p&gt;But guess what: my stupidity and not-knowingness in trying to build the impossible is exactly what landed me here. If I'd known from the start what it would take, &lt;strong&gt;80% of me would have said no&lt;/strong&gt;. It just so happens that I have most of the parts built, AI covers the missing technical-gap. As long as I made sure it did what it does responsibly.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI is a calculator, not math
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AI is transformative.&lt;/strong&gt; Whether you like it or not, whether you agree or disagree, whether you use it or not, whether the bubble is about to burst, it doesn't matter. As I see it, it's here to stay, and for a very long time.&lt;/p&gt;

&lt;p&gt;If you're a developer worried about losing your job to it, you shouldn't be. Let me explain why. At the very least, today I consider AI a calculator. A calculator helped people count, but that doesn't solve math. We've gone through many iterations of it: counting by hand, the abacus, the calculator, the computer, the server, and now AI. Through all those years, math is still a subject and teachers still exist today. It won't go away. What it does will just transform into a new form.&lt;/p&gt;

&lt;p&gt;If you ask me, &lt;strong&gt;it's designers&lt;/strong&gt; (&lt;strong&gt;like me, the ones just doing UX/UI&lt;/strong&gt;) who will most likely face massive job cuts first, because the new Claude Design has superseded Figma by a mile. It creates UX/UI so well, so quickly and so complete that customers will probably think you're incredibly fast and professional. I still remembered how I needed to make designs in Photoshop. Multiple 1GB psd file nightmare when the customer wanted to test 5 different header layout where you already complete 10+ pages. &lt;strong&gt;Horrible&lt;/strong&gt;. Sketch and Figma solved it elegantly (kudos to components and auto-layout), but wait until you wanted the design to be responsive, alive, animated and clickable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So don't worry fellow developers&lt;/strong&gt;. We still have thousand-miles of cybersecurity to worry about and plenty of legacy tech debt to cover in the coming years. Hahaha.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criticism welcome (I still don't like it)
&lt;/h2&gt;

&lt;p&gt;StoreFrame is also a way to find out what's right and what's wrong. Nobody likes criticism, including me. But praise alone won't get me anywhere. It's the customers' complaints, and being brave enough to face criticism, that do.&lt;/p&gt;

&lt;p&gt;When I first released it, it wasn't good enough. I agree. Even now I think there are many places that still need an update.&lt;/p&gt;

&lt;p&gt;Someone criticised running Docker with just single-user access as not secure. True. Having to wait 30 minutes for a new store is slow. Also true. So we got to work and fixed things along the way.&lt;/p&gt;

&lt;p&gt;Now we run three PHP containers instead of one: one for the web app, one for the PHP CLI and one for the deployer. We made the web-app container as read-only as possible. Then we added more: AppArmor and seccomp. Maybe soon we'll replace it with Podman and FrankenPHP. Whatever it needs to be, with an easy roll-out. Don't worry, it will be there.&lt;/p&gt;

&lt;h2&gt;
  
  
  You don't have to come in
&lt;/h2&gt;

&lt;p&gt;Business-wise, I'm not even close to a marketing or sales guy. If I wanted to make big bucks fast, the easy pick was to build something that sits next to, on top of, or alongside Hypernode or the other big names. Easier to promote, less friction.&lt;/p&gt;

&lt;p&gt;Because like me, and like you, developer: nobody &lt;em&gt;likes&lt;/em&gt; changing hosting, especially when you host many, many, many webshops. It's no fun, boring and it's risky (inc. getting out of comfort zone).&lt;/p&gt;

&lt;p&gt;I built StoreFrame because I needed it, the way I wanted it, the way I want to run it. It just happens that I open my front door. That doesn't mean you have to come in. Maybe you shouldn't, and it's okay too sticking with what you have.&lt;/p&gt;

</description>
      <category>magento</category>
      <category>hyva</category>
      <category>infrastructure</category>
      <category>automation</category>
    </item>
    <item>
      <title>10 Things I Learned at Meet Magento Germany (incl. PHP Vulnerability Disclosure)</title>
      <dc:creator>Laurentius Judhianto</dc:creator>
      <pubDate>Tue, 27 Oct 2026 10:45:11 +0000</pubDate>
      <link>https://dev.to/laurnts/10-things-i-learned-at-meet-magento-germany-incl-php-vulnerability-disclosure-3de9</link>
      <guid>https://dev.to/laurnts/10-things-i-learned-at-meet-magento-germany-incl-php-vulnerability-disclosure-3de9</guid>
      <description>&lt;p&gt;&lt;strong&gt;A PHP vulnerability dropped the night before Meet Magento Germany. Ten things I took home: about people, patching, merchants who choose offline, and why badges don't stop bugs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Last week I was at Meet Magento Germany. I came home with a full notebook, a tired body and one very unusual memory: a security disclosure for PHP itself landed the evening before the conference, while most of us were at the pre-party.&lt;/p&gt;

&lt;p&gt;Here are ten things I learned that week. Some are about people, some are about patching, and one is a promise.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Meeting the people you only read on LinkedIn or GitHub is awesome
&lt;/h2&gt;

&lt;p&gt;For years I've known a lot of this community as avatars: a commit on GitHub, a post on LinkedIn, a name in an issue thread. Shaking their hands is something else. You stop reading what people do and start hearing why they do it.&lt;/p&gt;

&lt;p&gt;Credits to the organizer of Meet Magento DE. Although the event is located in Mainz, Germany, everybody is talking in English. Everyone can just have open conversations - joining each other conversations. Stating the rules about pacman (if I recall it correctly) I think it's genius. It made everybody understand to be open and openup, ensuring that everybody is welcomed. Alot of respect to everyone, this is by far the most enjoyable event I have ever been for a long time.&lt;/p&gt;

&lt;p&gt;As for me, &lt;strong&gt;it's also something I have never imagine&lt;/strong&gt;, to be able to speak in such an event.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Disaster struck the day before, and everybody still had a great time
&lt;/h2&gt;

&lt;p&gt;Nobody look panicked, pre-party was lit. People knew what to check, who to call and what to roll out, and the next morning the conference went on as planned, with enthusiasm and very good conversations. That's what a mature community looks like: not one that never gets hit, but one that knows how to respond when it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. PHP showed us how vulnerable we all are
&lt;/h2&gt;

&lt;p&gt;Two critical vulnerabilities in the PHP interpreter itself, CVE-2026-109618 and CVE-2026-109619, now known as PHP2Shell. They allow remote code execution without logging in, they affect every PHP version from 5.4 to 8.5, and details leaked before the planned disclosure date. The fixes (8.5.12, 8.4.27, 8.3.36 and 8.2.35) arrived in a hurry on 21 October. PHP runs a huge part of the web, so a hole in the language is a hole in everything built on top of it: every framework, every CMS, every shop.&lt;/p&gt;

&lt;p&gt;The press already has a name for this month: Black October. PHP wasn't alone. In a few weeks we got:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;**PHP2Shell: **two unauthenticated remote code execution bugs in PHP itself (21 October).&lt;/li&gt;
&lt;li&gt;**Node2Shell: **remote code execution through Brotli decompression in Node.js, CVE-2026-111497 (21 October). Bun had the same problem.&lt;/li&gt;
&lt;li&gt;**Magento and Mage-OS: **Adobe's APSB26-153, an unauthenticated remote code execution scored CVSS 10 (13 October).&lt;/li&gt;
&lt;li&gt;**Next.js: **version 16.4.1 fixed remote code execution through image handling and React Server Components (14 October).&lt;/li&gt;
&lt;li&gt;**LiteLLM: **a supply-chain attack shipped a backdoored release to PyPI and Docker (21 October).&lt;/li&gt;
&lt;li&gt;**eMudhra: **intermediate certificate authority keys were compromised.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think we'll see this more often in the coming years, not less. The PHP flaws were found with the help of AI. Most of this code was written by humans alone, long before anyone reviewed code together with AI. Now researchers and attackers both have tools that read code at scale, and the old bugs in that human-only code are going to surface faster than they ever did.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Customers would rather go offline than take the risk
&lt;/h2&gt;

&lt;p&gt;I've never seen this before. When the news broke, merchants asked for their shops to go into maintenance mode until a fix was in place. They'd rather lose a day of sales than stay up and gamble.&lt;/p&gt;

&lt;p&gt;Not long ago the answer was always: &lt;strong&gt;keep it up at any cost, downtime is the enemy&lt;/strong&gt;. Now the enemy is a breach, the GDPR notification that follows it, and the customer trust that leaves with it. Merchants do that maths themselves today, and they do it fast.&lt;/p&gt;

&lt;p&gt;Part of it was FOMO, turned around. When everyone around you goes offline, staying online starts to feel like the risky choice. Customers watched government sites, broadcasters and hospitals go dark one after another, and they were afraid too. Nobody wanted to be the one shop still open when the attackers came looking.&lt;/p&gt;

&lt;p&gt;And for the first time, &lt;strong&gt;customers were writing PHP configuration suggestions to me&lt;/strong&gt;. They knew exactly which PHP version their own VM was running and whether it was patched yet. Security stopped being something they leave to us and became something they follow themselves. They checked this themself and know *&lt;em&gt;we have vPatch *&lt;/em&gt;(similar like what Cloudflare) used to help mitigate the situation. &lt;strong&gt;Yet again don't rely 100% on vPatch and WAF when the real fix is around, just fix it&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Moving every shop to Mage-OS 3.6.0 and PHP 8.4 was just in time
&lt;/h2&gt;

&lt;p&gt;A few weeks ago I decided to move every shop we run to Mage-OS 3.6.0 on PHP 8.4. Mage-OS 3.6.0 already carried the fix for October's critical Magento template-engine bug, so production was done by 20 October. One day later, PHP 8.4.27 came out (I wrote about &lt;a href="https://www.storeframe.io/blog/why-we-migrated-all-customers-to-mageos" rel="noopener noreferrer"&gt;why we moved everyone to Mage-OS&lt;/a&gt; earlier).&lt;/p&gt;

&lt;p&gt;Because every shop was on the same platform and the same PHP line, the patch was one job, not thirteen. The official Docker images weren't published yet, so we built them ourselves from the official recipe, checked the php.net source against its checksum and signature, and rolled the new version out to every shop on 21 October. One image, one rollout, one test plan. Being on the same version everywhere is boring right up until the night it saves you.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The sites that went down told us they run PHP
&lt;/h2&gt;

&lt;p&gt;Some of the sites taken offline that day gave away what they run underneath, just by going down. In the Netherlands that included DigiD, MijnOverheid and the website of the Tweede Kamer, the Dutch parliament. So now we all know: governments spend millions on PHP sites too.&lt;/p&gt;

&lt;p&gt;Even the website of the Dutch NCSC, the national agency that is supposed to inform everyone about vulnerabilities like this one, went offline, with a notice saying it had been taken down as a precaution because of the PHP vulnerabilities.&lt;/p&gt;

&lt;p&gt;And php.net, the home of the language itself, answered with a gateway timeout while the whole world was trying to read the advisory and download the fix.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr5ztyldxf726tscp3673.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr5ztyldxf726tscp3673.jpg" alt="21 October 2026: the Dutch NCSC, DigiD and php.net itself, all offline on the same day." width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's not a criticism of PHP, or of the NCSC. Offline as a precaution is not the same as hacked; it's exactly the choice from point 4. It's a reminder that boring technology carries the world, and that its patches deserve the same urgency as a zero-day in your own code.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Listening to everyone led me to something worth disclosing
&lt;/h2&gt;

&lt;p&gt;Talking and listening to so many people opened up new ideas. One conversation sent me digging somewhere I hadn't looked before, and I found something interesting, which I have disclosed to the Mage-OS and Sansec.&lt;/p&gt;

&lt;p&gt;I can't tell much yet. It has to do with a module leak that could be used as a way in. I'll write about it in a separate post later this week, so stay tuned.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Small companies defend on cost, big companies on people and process
&lt;/h2&gt;

&lt;p&gt;For smaller companies, security is a budget question. That's why so many still run WordPress on a shared cPanel box with Apache, or a shop that hasn't seen an update in two years. Not because they don't care, but because every update costs money they don't have.&lt;/p&gt;

&lt;p&gt;If you run a small business in the Netherlands, there is help with that budget. The NCSC's &lt;a href="https://www.ncsc.nl/subsidie" rel="noopener noreferrer"&gt;Mijn Cyberweerbare Zaak subsidy&lt;/a&gt; pays 50% of the cost of measures such as a password manager, two-factor authentication, patch management, tested backups or awareness training, up to €1,250 per business. It is open to freelancers and companies with up to 50 employees and €10 million turnover, until 30 November 2026, first come, first served.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdfu0f026xywn11gmkgpm.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdfu0f026xywn11gmkgpm.png" alt="The NCSC subsidy Mijn Cyberweerbare Zaak: up to €1,250 for small businesses, open until 30 November 2026." width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For bigger companies the money is there. Their weak spots are &lt;strong&gt;people and process - policies&lt;/strong&gt;. Every employee's laptop, phone, mailbox and access rights is a door that needs securing, and one leaked credential is enough: a key committed to a repository, a password reused by a contractor, a phishing mail opened on a phone. Attackers don't need a zero-day when they already have a login.&lt;/p&gt;

&lt;p&gt;And when the fix is ready, it still has to go through a lengthy deployment and approval process: a ticket, three sign-offs, next Tuesday's change window. The attacker doesn't wait for the change board.&lt;/p&gt;

&lt;p&gt;That's what MDR, XDR and EDR are for: managed, extended and endpoint detection and response. Software on every laptop, phone and server that watches what happens and raises the alarm, often with a team behind it watching around the clock. It's why you see &lt;a href="https://www.crowdstrike.com" rel="noopener noreferrer"&gt;CrowdStrike&lt;/a&gt; on the Mercedes and &lt;a href="https://www.sentinelone.com" rel="noopener noreferrer"&gt;SentinelOne&lt;/a&gt; on the Aston Martin every race weekend in Formula 1. Protecting people and process is big business, and it sells to big companies.&lt;/p&gt;

&lt;p&gt;And it goes deep. &lt;a href="https://www.tomshardware.com/tech-industry/cyber-security/north-korean-infiltrator-caught-working-in-amazon-it-department-thanks-to-lag-110ms-keystroke-input-raises-red-flags-over-true-location" rel="noopener noreferrer"&gt;Amazon caught a North Korean operative&lt;/a&gt; working as a remote systems administrator because his keystrokes took more than 110 milliseconds to reach Seattle, where a worker in the US would show well under 100. The laptop was in Arizona; the person typing was not. Amazon says it has stopped more than 1,800 such attempts since April 2024.&lt;/p&gt;

&lt;p&gt;Impressive, and a little uncomfortable. To measure how long every keystroke takes, you have to watch every keystroke. Somewhere between protecting the company and watching the employee there is a line on privacy, and in Europe, under GDPR, that line is not optional. The bigger the company, the harder it gets to defend against its own accounts without treating every employee as a suspect.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. The badges didn't keep anyone online, and attackers come in from the side
&lt;/h2&gt;

&lt;p&gt;NIS2, SOC 2, GDPR, ISO 27001. The certifications are expensive, and they matter: they prove you have a process. But they don't patch your PHP. Organisations with every shiny badge on their website still went offline that week.&lt;/p&gt;

&lt;p&gt;And look at where the most expensive attacks of the last two years actually came in. Rarely through the core system the certificate describes. Almost always through something around it: a supplier, a helpdesk, an employee.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;**bol.com (August 2026): **attackers got into two order-processing systems at its logistics partner CEVA Logistics, not at bol itself. Names, addresses, phone numbers and order details of bol and De Bijenkorf customers ended up for sale on the dark web.&lt;/li&gt;
&lt;li&gt;**Intermarché (October 2026): **the refrigeration in around 150 French stores was hacked through Danfoss cooling controllers, many of them legacy units that no longer get security updates. Fridges and freezers were pushed into nonstop defrost, and the stores had to throw away their fresh and frozen food. The system was run by the supplier, not by the retailer.&lt;/li&gt;
&lt;li&gt;**Marks &amp;amp; Spencer (April 2025): **attackers impersonated an employee and talked the outsourced IT helpdesk into resetting credentials. The ransomware that followed shut down online orders for weeks.&lt;/li&gt;
&lt;li&gt;**Odido (February 2026): **attackers posed as the IT department and got employees to approve a login to the customer system. Records of up to six million customers were exposed.&lt;/li&gt;
&lt;li&gt;**Qantas, Allianz Life and others (2025): **employees were tricked into handing over access to cloud CRM systems connected to Salesforce. Qantas alone lost nearly six million customer records.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these was a missing patch in the main application. The certificate covered the castle; the attackers came in through the delivery entrance, the cold storage and the phone at the helpdesk.&lt;/p&gt;

&lt;p&gt;Not even a government with every obligation in the book is spared. In August 2026 the ransomware group Rhysida spent a week inside the networks of two Berlin Senate departments, urban development and mobility, before anyone shut them down. Berlin refused the ransom of 30 bitcoin, roughly two million euros, and in September 1.4 million files appeared on the dark web.&lt;/p&gt;

&lt;p&gt;Look at what was in them: payroll lists, bank details and scans of employees' ID documents. That is how one breach feeds the next. As the Chaos Computer Club put it, the more you know about a person, the better you can impersonate them. Today's leak is tomorrow's convincing call to the helpdesk.&lt;/p&gt;

&lt;p&gt;Compliance tells you what to do. It can't make a new vulnerability wait for your next audit, and it can't stop a tired employee from approving one login too many. What counts is how well you know every system, supplier and person that can reach your data, and how fast you can act when one of them fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. I'll be sharing more of the why
&lt;/h2&gt;

&lt;p&gt;Most importantly: the conference reminded me that people don't just want to know what we built, they want to know why. So I'll be writing more about the engineering and architecture decisions behind StoreFrame. Why every shop runs the same stack, why we build our own images, why we moved to Mage-OS, and what we got wrong along the way.&lt;/p&gt;

&lt;p&gt;Thank you to everyone at Meet Magento Germany for the conversations, and to the organisers for keeping it a great event on a very long night.&lt;/p&gt;

&lt;p&gt;There is so much more from the event worth writing about and publishing. Right now it's just a matter of finding the time and energy to turn it all into good articles. Stay tuned.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>magento</category>
      <category>mageos</category>
    </item>
    <item>
      <title>After Building StoreFrame in NEXT.js, Headless Magento Will Make a Big Comeback</title>
      <dc:creator>Laurentius Judhianto</dc:creator>
      <pubDate>Wed, 14 Oct 2026 09:50:55 +0000</pubDate>
      <link>https://dev.to/laurnts/after-building-storeframe-in-nextjs-headless-magento-will-make-a-big-comeback-d52</link>
      <guid>https://dev.to/laurnts/after-building-storeframe-in-nextjs-headless-magento-will-make-a-big-comeback-d52</guid>
      <description>&lt;p&gt;&lt;strong&gt;PWA Studio, Vue Storefront, GraphCommerce, Rapidez, Breeze and Hyva: why headless Magento kept losing to cost, and why AI that writes JavaScript fluently may finally flip the verdict.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Headless Magento had a moment, and then it had a funeral. Ask my ex-colleagues about it. Magento developers have been chasing Lighthouse scores and a faster frontend since 2013, and most of us went back to the stack we already knew.&lt;/p&gt;

&lt;p&gt;I was one of them: a Magento frontend developer stuck in the PHP world, knowing only a tiny bit of jQuery. NEXT.js was a foreign object. Then I built StoreFrame’s hub, and this site, &lt;a href="https://www.storeframe.io" rel="noopener noreferrer"&gt;www.storeframe.io&lt;/a&gt;, in NEXT.js. It is my first NEXT.js project. No joke. Somewhere in that build I changed my mind about the old headless debate, and the reason is sitting in my own codebase.&lt;/p&gt;

&lt;p&gt;To explain why, we have to start with the original attempt, and why every attempt after it ran into the same wall.&lt;/p&gt;

&lt;h2&gt;
  
  
  Venia and PWA Studio: the original headless Magento
&lt;/h2&gt;

&lt;p&gt;When Magento shipped &lt;a href="https://developer.adobe.com/commerce/pwa-studio/" rel="noopener noreferrer"&gt;PWA Studio&lt;/a&gt; with Venia as its reference storefront, it was the official answer to a slow Luma frontend: a React application, talking to Magento through GraphQL, with its own build pipeline and its own deploy. On paper it was the clean architecture. Magento becomes a commerce API, the storefront becomes a modern JavaScript app.&lt;/p&gt;

&lt;p&gt;In practice, the complaint from web developers was always the same: you now needed two experienced developers. One who knows React, webpack and GraphQL. One who knows PHP, dependency injection and Magento’s module system. And those two people had to agree on every field, every resolver and every checkout step.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu96ferqljjad3a44ppr9.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu96ferqljjad3a44ppr9.jpg" alt="Headless Magento, circa 2019: every bug lives exactly between two teams." width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It got worse with extensions. The Magento module ecosystem was built for Luma. Every payment method, every shipping rule, every third-party module you bought had to expose its data over GraphQL and get a React counterpart before it worked in Venia. Many never did, so somebody wrote that bridge, by hand, store by store.&lt;/p&gt;

&lt;p&gt;Adobe has since moved on. PWA Studio still gets maintenance and compatibility releases, but new storefront projects are pointed toward Edge Delivery Services instead. The original headless Magento is not dead. It is just no longer where anyone is building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vue Storefront: the community’s answer
&lt;/h2&gt;

&lt;p&gt;Vue Storefront came out of the same frustration, from developers who wanted the Magento frontend fast and flexible, in Vue instead of React. For a while it was the exciting option, with a busy community and agencies building on it.&lt;/p&gt;

&lt;p&gt;But it inherited the same bill: a separate JavaScript application, its own release cycle, and an integration layer to keep in sync with Magento. Over time the project went platform-agnostic, added a React kit, and in March 2024 &lt;a href="https://alokai.com/news/vue-storefront-is-now-alokai" rel="noopener noreferrer"&gt;rebranded as Alokai&lt;/a&gt;, positioned as a commercial frontend platform for many commerce backends. That may be a sound business. As the default way to build a Magento frontend, it never happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  The alternatives that stayed
&lt;/h2&gt;

&lt;p&gt;After those two, the ecosystem split into tools that chose a side: either fully decoupled, or as close to Magento’s own templates as possible.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F12d6xa2almxskbb74hon.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F12d6xa2almxskbb74hon.jpg" alt="Four answers to the same question: GraphCommerce, Rapidez, Breeze and Hyva." width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphCommerce
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.graphcommerce.org/" rel="noopener noreferrer"&gt;GraphCommerce&lt;/a&gt; is a React and NEXT.js storefront that talks to Magento through GraphQL only. It runs as a separate application and is the most modern take on the Venia idea. It is also, honestly, still two stacks: you need the React side and the Magento side, and the GraphQL contract in between.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rapidez
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/rapidez/rapidez" rel="noopener noreferrer"&gt;Rapidez&lt;/a&gt; takes a different route: Laravel, Blade and Vue, reading catalog data straight from Magento’s database and Elasticsearch, with GraphQL for the rest. PHP developers feel at home in it. But it is still a second application next to Magento, with its own deploy and its own upgrade path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Breeze
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://breezefront.com/" rel="noopener noreferrer"&gt;Breeze&lt;/a&gt; goes the other way. It keeps Luma’s templates and layout and swaps the heavy JavaScript underneath, RequireJS and Knockout, for a much lighter runtime. Most Luma-compatible modules keep working. No second application, no second team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hyva
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.hyva.io/" rel="noopener noreferrer"&gt;Hyva&lt;/a&gt; is the one that won, at least this round. Tailwind and Alpine.js, a fraction of the bytes, a good Lighthouse score out of the box, and the .phtml and XML layout every Magento developer already knew. One lean stack instead of two. Since November 2025 the theme itself is free and open source.&lt;/p&gt;

&lt;p&gt;There is a reason everyone in this industry knows Willem Wigman, Hyva’s founder. In fifteen years, only a handful of people have been able to show real work on both sides, frontend and backend. Hyva won because it did not ask every team to find one of those people.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why headless died: cost, not architecture
&lt;/h2&gt;

&lt;p&gt;So let’s be honest about why decoupled Magento didn’t win. A fast frontend talking to Magento purely as a commerce API is a good architecture. It died on cost and complexity, which is how most good architectures die.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Most PHP developers don’t do JavaScript&lt;/strong&gt;. &lt;strong&gt;Most frontend developers who know a bit of PHP don’t do React either&lt;/strong&gt;. And plenty of experienced Magento backend developers can’t centre a div inside a div. So headless meant two hires, two skill sets and two codebases to keep in step, forever. Hyva gave most of the speed for one. Why on earth run two stacks when one gets you nearly there?&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu87m23lo4lz2047g6ium.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu87m23lo4lz2047g6ium.jpg" alt="The real reason headless lost: you had to press both buttons." width="600" height="908"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed: AI speaks JavaScript fluently
&lt;/h2&gt;

&lt;p&gt;Here is why I think the funeral was premature. Ask the obvious question: &lt;strong&gt;what is an AI agent almost unfairly good at&lt;/strong&gt;? JavaScript. TypeScript. React. NEXT.js. Tailwind. The exact stack a decoupled frontend is built from.&lt;/p&gt;

&lt;p&gt;That is not an accident. JavaScript and TypeScript sit at the top of GitHub’s language rankings year after year, and the frontend world has rewritten itself every few years: jQuery, Backbone, Angular, React, Vue, Svelte, NEXT.js. Each wave left behind millions of public repositories, tutorials, Stack Overflow answers and migration guides. Models learned from all of it. Magento’s XML layout and .phtml conventions are a small corner of the internet by comparison.&lt;/p&gt;

&lt;p&gt;For a frontend developer, that is a relief. The churn that used to drive us crazy, a new framework, a new router, a new build tool every two years, is exactly what the agent has already read. You describe the component; it knows the idiom of whichever version you happen to be on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web fundamentals: the copy-paste Lighthouse 100
&lt;/h2&gt;

&lt;p&gt;This is the part nobody wanted to say out loud. With a headless NEXT.js frontend, getting an almost, if not perfect, 100 Lighthouse score is close to mechanical. &lt;strong&gt;&lt;strong&gt;Run Lighthouse&lt;/strong&gt;&lt;/strong&gt;&lt;strong&gt;. **__&lt;/strong&gt;Copy the report*&lt;em&gt;__&lt;/em&gt;&lt;em&gt;. *&lt;/em&gt;&lt;strong&gt;&lt;strong&gt;Paste it into the prompt&lt;/strong&gt;&lt;/strong&gt;&lt;strong&gt;.&lt;/strong&gt; The agent fixes what it lists: the LCP image without priority, the font that isn’t preloaded, the image without dimensions that shifts the layout, the JavaScript that ships but never runs. Run it again.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6r4g3psq6bsduogpwi2j.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6r4g3psq6bsduogpwi2j.jpg" alt="Web performance, in four stages of enlightenment." width="800" height="1122"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works because in a headless frontend you own every byte on the page. Every Lighthouse finding points at code in your repository, in a framework the agent knows inside out. In a classic Magento theme the same report points into RequireJS bundles, third-party module templates and inline scripts you don’t own and can’t safely change. That loop is how I tuned this site, and it took minutes, not a sprint.&lt;/p&gt;

&lt;p&gt;The fundamentals didn’t change: ship less JavaScript, size your images, load fonts properly, don’t block the main thread. What changed is the cost of applying them. &lt;strong&gt;It used to take a frontend specialist a weeks or months tweaking everything and breaking it over an upgrade&lt;/strong&gt;. &lt;strong&gt;Now it takes an agent a coffee break&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend and backend can finally work together
&lt;/h2&gt;

&lt;p&gt;Remember the two-developer problem from Venia: two people, two stacks, and a GraphQL contract nobody wanted to own. That contract is where most headless projects bled time.&lt;/p&gt;

&lt;p&gt;Now creating an API is a few prompts away. Need a GraphQL resolver for a custom module, or a REST endpoint for a checkout field? Describe it, let the agent write it, read the diff. Need the frontend to consume it? The same agent reads the schema and writes the typed client and the component. The backend developer doesn’t have to learn React, and the frontend developer doesn’t have to learn dependency injection. They meet at the contract, and the contract is cheap.&lt;/p&gt;

&lt;p&gt;That is what happened to me. A Magento frontend developer with a bit of jQuery built a full NEXT.js product, with its API, websockets and dashboard, because the agent filled the half of the stack I never learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Design: the Figma handoff disappears
&lt;/h2&gt;

&lt;p&gt;The last piece is design. Claude Design, which Anthropic launched in April 2026, turns a prompt, a screenshot or your existing design system into a working prototype. Personally, I think it is a Figma killer, and the market briefly seemed to agree: Figma’s stock fell about 7% on launch day.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhkfa3gjb30nh6qapokoj.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhkfa3gjb30nh6qapokoj.jpg" alt="Every frontend developer who has rebuilt a Figma file by hand." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reason matters more than the stock price. A Figma file is a picture of an interface that somebody still has to rebuild in code. What Claude Design produces is already HTML, CSS and JavaScript, and it hands off to Claude Code. Turning that into React components for a NEXT.js storefront is a short step, because it was never a drawing in the first place. Design, frontend and the API it calls now sit in one loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I picked NEXT.js over anything else
&lt;/h2&gt;

&lt;p&gt;If AI writes the JavaScript, why NEXT.js and not something else? Fair question. Here is my honest answer, in four parts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Shadcn, nothing more and nothing less.&lt;/strong&gt; &lt;a href="https://ui.shadcn.com/" rel="noopener noreferrer"&gt;shadcn/ui&lt;/a&gt; is the component library I am sworn to. It was born in React and NEXT.js; the Svelte and Vue versions are ports. I want to be on the original, not the copy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ecosystem is more polished.&lt;/strong&gt; Animation libraries like Motion (which started life as Framer Motion), frameworks, starter kits and tooling all land in React and NEXT.js first, and they are more finished there than anywhere else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit beats clever when AI writes it.&lt;/strong&gt; Svelte might be easier, and Nuxt might be a lot better in many ways, if I were writing the JavaScript myself. But the agent writes most of it. When the agent writes and reads every line, an explicit, even verbose component it can see all of matters more than an elegant abstraction that hides what is going on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The AI tools already speak React.&lt;/strong&gt; Guess what v0, Vercel and Claude Design produce? React. When the tools that generate interfaces all default to the same stack, that is a strong hint about what the models know best.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And StoreFrame’s NEXT.js app isn’t another Hello World portfolio site. It isn’t even a simple CRUD app wrapped in auth and payments. Most of what happens inside it runs over websockets: live consoles, live deploy logs, live metrics streaming in from every store.&lt;/p&gt;

&lt;p&gt;Almost nobody in the PHP world builds websockets. Magento leans on caching, and real-time is the exact opposite of a cache. So after taming that beast with an agent at my side, I am more than confident that NEXT.js is a solid choice when AI is writing the JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is this Hyva’s peak?
&lt;/h2&gt;

&lt;p&gt;Every Magento frontend has had its period. Luma had one. PWA Studio had one. Vue Storefront had one. So it’s fair to ask whether Hyva will have its period too, and whether that is something to worry about.&lt;/p&gt;

&lt;p&gt;Not tomorrow. Hyva is still a safe, fast choice for most merchants today, and nobody needs to panic about a store running on it. I’m not telling anyone to re-platform onto a headless build this afternoon.&lt;/p&gt;

&lt;p&gt;But I’m skeptical it is the endgame. Every Magento frontend so far has been a bet on a niche skill: Knockout for Luma, React wired into GraphQL for Venia, .phtml with Alpine for Hyva. The pool of developers, and the training data behind every AI model, is overwhelmingly JavaScript. When the cost of a JavaScript frontend collapses, a stack built to avoid JavaScript loses its main reason to exist. At times, I'm as skeptical as staying on Magento too.&lt;/p&gt;

&lt;h2&gt;
  
  
  I’m on the headless bandwagon, 1000%
&lt;/h2&gt;

&lt;p&gt;The thing that buried headless was economics, not architecture, and the economics just changed. When an agent can write the decoupled frontend, the API between the two halves and the design it starts from, the overhead stops being the dealbreaker it was. &lt;strong&gt;AI writes JavaScript like Magnus Carlsen plays blindfold chess&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So yes, I’m definitely on the bandwagon. StoreFrame’s own frontend already is: a NEXT.js app with the backend behind an API. Give the agents a few more turns of the crank, and a split frontend-and-backend Magento stops being the brave choice and becomes the sensible one.&lt;/p&gt;

&lt;p&gt;I said in &lt;a href="https://www.storeframe.io/blog/luma-faster-than-hyva-on-hypernode" rel="noopener noreferrer"&gt;an earlier post&lt;/a&gt; that this was a story for another day. This is me starting to tell it. It isn’t mainstream yet. But for the first time in years, I’m betting on it.&lt;/p&gt;

&lt;p&gt;We build StoreFrame’s own frontend in NEXT.js and run Magento underneath it — so when the headless math flips for real, we’ll be ready. Curious how we think about the stack?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.storeframe.io" rel="noopener noreferrer"&gt;See how StoreFrame works&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>magento</category>
      <category>headless</category>
      <category>frontend</category>
      <category>pwa</category>
    </item>
    <item>
      <title>Why We Migrated All Our Customers to Mage-OS. What Is Mage-OS Anyway, Compared to Magento 2?</title>
      <dc:creator>Laurentius Judhianto</dc:creator>
      <pubDate>Wed, 07 Oct 2026 08:26:39 +0000</pubDate>
      <link>https://dev.to/laurnts/why-we-migrated-all-our-customers-to-mage-os-what-is-mage-os-anyway-compared-to-magento-2-2min</link>
      <guid>https://dev.to/laurnts/why-we-migrated-all-our-customers-to-mage-os-what-is-mage-os-anyway-compared-to-magento-2-2min</guid>
      <description>&lt;p&gt;&lt;strong&gt;Every Magento merchant asks whether the platform will still be here, and still be free, in five years. We stopped answering shop by shop and moved every customer to Mage-OS. Here's why.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every Magento merchant I know has asked the same question at least once in the last few years: is this platform still going to be here, and still be free, in five years? Answering it shop by shop got old. So we stopped answering it shop by shop, and moved every customer we run onto Mage-OS.&lt;/p&gt;

&lt;p&gt;This is why, what Mage-OS actually is, and, to be fair, what still annoys me about the platform after all these years.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Mage-OS anyway?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://mage-os.org/" rel="noopener noreferrer"&gt;Mage-OS&lt;/a&gt; is a community distribution of Magento Open Source. It started with the open letter The Future of Magento in September 2021, signed by more than 1,600 people, and is run by a registered non-profit association. The first distribution, 1.0.0, shipped in October 2023 on top of Magento 2.4.6.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mage-os.org/releases/2026-05-18-mage-os-3-0-0-release" rel="noopener noreferrer"&gt;Mage-OS 3.0&lt;/a&gt; (May 2026) is built on Magento 2.4.9. So for a merchant it is still Magento 2: same architecture, same admin, and it aims to stay compatible with the extensions you already use. What changes is who decides what goes in, and how fast.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Magento Open Source&lt;/strong&gt; is the free edition Adobe publishes alongside its paid Adobe Commerce. Adobe sets the roadmap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mage-OS&lt;/strong&gt; takes that same core, keeps it compatible, and adds what the community agrees on: an interactive installer, admin activity logging, returns (RMA), AI-assisted translations, PCI DSS 4.0 features, and tools like n98-magerun2 in the box. No Adobe account needed to install it.&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdgyxe7efvy8yo3fqndzi.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdgyxe7efvy8yo3fqndzi.jpg" alt="Same Magento. Different people deciding." width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we moved every customer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The community is small, global and actually talks to each other
&lt;/h3&gt;

&lt;p&gt;Mage-OS has regular technical calls on Discord, with developers from all over the world, and hackathons where people build genuinely cool things, like the one Made by Mouses hosted in Zwolle on 24 September 2026. If you are curious where the platform is heading, keep an eye on Mage-OS 4.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg0r17l9606jx9n1kqpuy.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg0r17l9606jx9n1kqpuy.jpg" alt="The Mage-OS hackathon in Zwolle, 24 September 2026, hosted by Made by Mouses." width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Your background matters less than your contribution
&lt;/h3&gt;

&lt;p&gt;I am not a backend developer who can be compared to some of the veterans in there. But validating a bug, writing a clear issue and proposing a possible fix already helps. With AI today that isn't hard. What it does mean is checking three times before posting: is the fix right, is it really the root cause, and is there already a ticket for it? Nobody wants AI slop in their issue tracker.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The AI work coming next is worth waiting for
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://mage-os.org/updates/2026-07-01-june-update" rel="noopener noreferrer"&gt;Mage-OS 4 roadmap&lt;/a&gt; puts AI at the centre: a shared AI foundation, merchant tools, catalog generation, AI-powered search and recommendations. Next to that, &lt;a href="https://askmago.com/" rel="noopener noreferrer"&gt;Mago&lt;/a&gt;, a free open-source AI admin assistant built together with Mage-OS Nederland, already runs on Mage-OS 3. I'm watching that one closely, because it is exactly what we do at StoreFrame: AI agents doing the operations work.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Batteries included saves time and money
&lt;/h3&gt;

&lt;p&gt;This one is personal. Most things should simply be there on day one, ready to use or automated. I hate day-two tweaking for things that could have worked the second the shop was installed. Mage-OS keeps pulling those day-two jobs into the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Adobe uncertainty
&lt;/h3&gt;

&lt;p&gt;This one has been coming for a long time. A short history:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;**2015: **Magento 2 goes GA, and Magento 1 merchants are told to plan a migration.&lt;/li&gt;
&lt;li&gt;**2018: **Adobe buys Magento for $1.68 billion. Magento 1 support was meant to end in November 2018; it was stretched to June 2020, and on 30 June 2020 it ended. Many merchants rebuilt their whole shop because of a date they did not choose.&lt;/li&gt;
&lt;li&gt;**2021: **Magento Commerce becomes Adobe Commerce.&lt;/li&gt;
&lt;li&gt;**2025: **Adobe announces Adobe Commerce as a Cloud Service and Adobe Commerce Optimizer, both SaaS.&lt;/li&gt;
&lt;li&gt;**Today: **Adobe's lifecycle and release pages are written for Adobe Commerce. Magento Open Source is barely mentioned.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Adobe has not said Magento Open Source is going away. It just says very little about it, and its energy clearly goes into SaaS. After the Magento 1 deadline, we would rather not bet our customers' shops on silence again. Mage-OS keeps the open-source core in community hands, whatever Adobe decides.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. It's going to get faster and more adaptable
&lt;/h3&gt;

&lt;p&gt;Mage-OS ships features every few months, and things like a &lt;a href="https://packagist.org/packages/mage-os/module-worker-mode" rel="noopener noreferrer"&gt;worker-mode module for FrankenPHP&lt;/a&gt; are being built in the open. Some people clearly put a lot of their own time into this. Kudos to the Mage-OS board and the community.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I still don't like about Magento
&lt;/h2&gt;

&lt;p&gt;It wouldn't be fair to only praise it. After all these years, here is what still bugs me.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Installing and automating it from scratch is still painful
&lt;/h3&gt;

&lt;p&gt;We solved it with our own wizard, and Mage-OS 3 added an interactive installer, which helps. But it is not at WordPress level yet, even with all these awesome people working on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Uncached pages are still slow, especially in the admin
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://packagist.org/packages/qoliber/nebula-admin-theme" rel="noopener noreferrer"&gt;Nebula&lt;/a&gt;, a modern admin theme from Qoliber built on Alpine.js and Tailwind, might be the thing that finally fixes the admin. But if I had to choose, the Laravel Filament way is a much better way to build an admin UI.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffm6uxedkdvejp8hjnw77.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffm6uxedkdvejp8hjnw77.jpg" alt="Every uncached admin page, every time." width="720" height="709"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. We should be heading to worker mode
&lt;/h3&gt;

&lt;p&gt;FrankenPHP or OpenSwoole, with the application kept warm between requests. In our own tests FrankenPHP worker mode made single requests faster, but it is still nowhere near Filament or SPA speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. It's not an SPA or PWA
&lt;/h3&gt;

&lt;p&gt;Yes, yes, this one is personal preference. I don't like JavaScript and I will never write it by hand. But AI has been great at writing it, and coming from a UX and UI background, I think a fluent interface is one of the most important parts of the shopping experience and of managing a webshop. The micro-animations, the fluidity, the consistency of libraries like shadcn are priceless.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, Mage-OS
&lt;/h2&gt;

&lt;p&gt;None of this made us want to leave Magento. It made us want Magento in hands that will still be maintaining it, openly, in ten years. For us that is Mage-OS.&lt;/p&gt;

</description>
      <category>magento</category>
      <category>mageos</category>
      <category>opensource</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Self-Hosted Proof of Work with ALTCHA: A Turnstile Alternative for Magento</title>
      <dc:creator>Laurentius Judhianto</dc:creator>
      <pubDate>Tue, 06 Oct 2026 12:48:42 +0000</pubDate>
      <link>https://dev.to/laurnts/self-hosted-proof-of-work-with-altcha-a-turnstile-alternative-for-magento-3n0h</link>
      <guid>https://dev.to/laurnts/self-hosted-proof-of-work-with-altcha-a-turnstile-alternative-for-magento-3n0h</guid>
      <description>&lt;p&gt;&lt;strong&gt;One decision saved us setup time on every store, kept catalogue pages fast and stopped more bots: a self-hosted, Turnstile-style challenge that installs itself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part 5 of 5 in the anti-bot series. New here? Start with Part 1, the overview: *&lt;a href="https://www.storeframe.io/blog/magento-anti-bot-anti-scraping-guide" rel="noopener noreferrer"&gt;Magento Anti-Bot and Anti-Scraping Guide&lt;/a&gt;&lt;/em&gt;.*&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cloudflare.com/application-services/products/turnstile/" rel="noopener noreferrer"&gt;Cloudflare Turnstile&lt;/a&gt; is a lovely idea. Most visitors never see a puzzle. Their browser quietly proves itself in the background, and only the doubtful ones get asked anything. No traffic lights, no crosswalks, no squinting at a bicycle wondering whether the handlebar counts.&lt;/p&gt;

&lt;p&gt;I wanted exactly that for every Magento store we run. Without the Cloudflare part, and, more importantly, without anyone having to set it up.&lt;/p&gt;

&lt;p&gt;This post is about the one decision that ended up saving us time every day, making stores faster and making them safer, all at once. Three wins from one fairly boring decision. Those are my favourite kind.&lt;/p&gt;

&lt;h2&gt;
  
  
  The chore nobody puts on the roadmap
&lt;/h2&gt;

&lt;p&gt;A captcha sounds like a five-minute job. For one store, it is.&lt;/p&gt;

&lt;p&gt;Turnstile is set up per site, by a person: create the account, generate the keys, wire the widget into the store, register the domains. Then do it again for the next store. And the next one. And the staging copy of the next one.&lt;/p&gt;

&lt;p&gt;That list hides the worst part: the sign-up. Before a single shopper sees a checkbox, Turnstile wants an account: register, confirm your email, add the domain, generate a site key and a secret key, install a Magento module to wire it all in, then discover the staging domain needs its own entry. All of that for a little box that says I am not a robot.&lt;/p&gt;

&lt;p&gt;Then the back and forth starts. Emails to confirm, API keys to pass along, a module to install and keep updated. One person has the login, another has the store, and the keys live in an email thread nobody can find six months later. A domain changes and the whole dance starts again. Do that for a fleet of stores and you have a part-time job that nobody applied for.&lt;/p&gt;

&lt;p&gt;Multiply that by every store you run and a captcha you set up by hand becomes a captcha someone, one day, forgets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shopping for an engine
&lt;/h2&gt;

&lt;p&gt;So we went shopping. It turned out to be harder than building one, which we had already tried. Every option was good at something and wrong for us in a different way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.google.com/recaptcha" rel="noopener noreferrer"&gt;Google reCAPTCHA&lt;/a&gt; ships with Magento, so it was the obvious default. It also sends visitor data to Google, and on a bad day it asks your shoppers to find the bicycles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cloudflare.com/application-services/products/turnstile/" rel="noopener noreferrer"&gt;Cloudflare Turnstile&lt;/a&gt; had exactly the right idea. But every store means the same round trip: an account, emails, API keys and a Magento module to install and keep updated, plus a vendor in the checkout path. Time and effort we did not want to spend, over and over.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TecharoHQ/anubis" rel="noopener noreferrer"&gt;Anubis&lt;/a&gt; is a whole proxy that puts every visitor through the puzzle. Brilliant for a code forge drowning in AI crawlers. Too blunt for a shop, where the visitor you challenge is often the one about to pay.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://trycap.dev" rel="noopener noreferrer"&gt;Cap&lt;/a&gt; came closest: open source, self-hosted, no image puzzles. It is deployed as a service of its own, though, which means one more thing to run and keep updated next to every store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://altcha.org/" rel="noopener noreferrer"&gt;ALTCHA&lt;/a&gt; won on the boring things. It is open source, self-hosted and collects nothing, and it ships official libraries in both Go and PHP, the two languages our two layers already speak. So the same protocol runs twice on every store, by default: once at the edge, in front of everything, and once inside Magento, on the storefront forms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Win one: time. It installs itself.
&lt;/h2&gt;

&lt;p&gt;Both copies of ALTCHA are part of the store from the moment it is provisioned. The edge one ships inside our OpenResty container; the Magento one ships as a module in every shop's base package. No accounts, no keys, no widget to wire in, no checklist.&lt;/p&gt;

&lt;p&gt;Every store runs the same protection, at the same version, from day one. Nobody has to go through a captcha integration, and nobody can forget to.&lt;/p&gt;

&lt;p&gt;That is the daily saving. Not one big afternoon, but a long tail of small chores that simply never appear on anyone's list again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Win two: speed. Bots pay before PHP does.
&lt;/h2&gt;

&lt;p&gt;Here is the part people do not expect: the security layer made stores faster.&lt;/p&gt;

&lt;p&gt;A cached product page is almost free. Catalogue search, or a category filtered by five attributes at once, is real database work. Bots love those pages the way toddlers love lift buttons: every combination, again, forever.&lt;/p&gt;

&lt;p&gt;So a visitor who lands on catalogue search cold, with no history on the store, solves a small puzzle first. A shopper never notices; their browser does it while they read. The first burst this met came from 1,477 addresses. None of it reached PHP, and the server's load dropped from about 12.6 to 3.2.&lt;/p&gt;

&lt;p&gt;On deep layered-navigation filters the price is dynamic: when a store is being hammered, the puzzle gets harder for everyone who has not earned trust yet. The share of abusive filter requests reaching PHP went from nearly all of them to 0.025%, with no real shoppers caught.&lt;/p&gt;

&lt;p&gt;Every request the edge turns away is a PHP worker your shoppers get to keep.&lt;/p&gt;

&lt;h2&gt;
  
  
  Win three: security. Bots pay a toll.
&lt;/h2&gt;

&lt;p&gt;If you read &lt;a href="https://www.zdnet.com/article/fed-up-with-ai-scraping-your-content-this-open-source-bot-blocker-can-help-heres-how/" rel="noopener noreferrer"&gt;ZDNet's piece on Anubis&lt;/a&gt;, the open-source blocker that makes every visitor solve a proof-of-work puzzle, you will recognise the idea. Anubis is great, and honest about its limits: its own README calls it &lt;a href="https://github.com/TecharoHQ/anubis" rel="noopener noreferrer"&gt;a bit of a nuclear response&lt;/a&gt;. In August 2025, &lt;a href="https://www.theregister.com/software/2025/08/16/codeberg-beset-by-ai-bots-that-now-bypass-anubis-defense/1023101" rel="noopener noreferrer"&gt;Codeberg reported that AI crawlers had learned to solve its challenges&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We saw the same. One swarm solved our challenge 2,490 times out of 2,490. Politely. Instantly.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw0yf1kq2iz4un6nlz2ml.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw0yf1kq2iz4un6nlz2ml.jpg" alt="Every swarm we have met, roughly. Photo: Wikimedia Commons \(CC0\)." width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is not a failure of proof of work, it is a misunderstanding of it. Proof of work is not a wall. It is a toll. A shopper pays it once, in a moment of CPU they never notice. A scraper pays it on every fresh session, forever, at whatever price the store is currently charging. And while it pays, every other layer is still watching: the fingerprints, the scoring, the crawler checks, the firewall.&lt;/p&gt;

&lt;p&gt;Across multiple production stores over a given period, 404,149 visitors were shown the full challenge page, and &lt;strong&gt;85.6% of them never solved it&lt;/strong&gt;. The click-to-verify version, reserved for the highest scores, was solved by 3.1%. Which tells you who was getting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  When we ask, and when we don't
&lt;/h2&gt;

&lt;p&gt;The Anubis approach puts every visitor through the puzzle. For a shop, that is a conversion tax on the people you most want to keep. Nobody in history has ever checked out faster because of a captcha. So we only ask when:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is a browser's first visit&lt;/strong&gt;, invisibly, on the real page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The score is doubtful&lt;/strong&gt;, also invisibly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The score is high&lt;/strong&gt;, with a short interstitial that solves itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A cold visitor asks for an expensive page&lt;/strong&gt;, before the query runs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The store is under pressure&lt;/strong&gt;, at a higher price for everyone not yet trusted.&lt;/p&gt;

&lt;p&gt;Verified search engines never see it. Returning shoppers with a valid pass do not see it again for a while. And inside Magento, ALTCHA also replaces &lt;a href="https://developers.google.com/recaptcha" rel="noopener noreferrer"&gt;reCAPTCHA&lt;/a&gt; on the storefront forms, so they get the same treatment without a single traffic light.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we got here, in three versions
&lt;/h2&gt;

&lt;p&gt;Before any of this, the defence was rate limits and block lists. It had exactly two words in its vocabulary, yes and no, and was very confident about both. What was missing was a third answer: maybe. Prove it.&lt;/p&gt;

&lt;p&gt;**Version one **was homegrown. Launch day took about twenty-five commits, most of them us changing our minds about who deserved a puzzle. I am not proud of that number.&lt;/p&gt;

&lt;p&gt;**Version two **swapped our own cryptography for ALTCHA, because writing your own crypto is the kind of hobby that ends in a blog post titled lessons learned. Along the way we tried a classic squiggly-letters captcha and dropped it quickly: modern text recognition reads squiggly letters better than people do, so it was a test only humans could fail.&lt;/p&gt;

&lt;p&gt;**Version three **settled on one rule: solving the puzzle is the browser test. Everything else only decides how hard we ask, never whether a real browser gets the chance to answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Was it worth not using Turnstile?
&lt;/h2&gt;

&lt;p&gt;Honest answer: yep, 100%. Turnstile has something we never will: signal from a huge slice of the internet. If you are on Cloudflare already, use it and go outside.&lt;/p&gt;

&lt;p&gt;We are not. For us, owning it means no third-party script on any page, no vendor in the checkout, no visitor data leaving the store, and a challenge we can tune per store when an attack changes shape. The price was three versions, a couple of embarrassing bugs.&lt;/p&gt;

&lt;p&gt;I would do it again. I would just do it in one version instead of three.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Numbers in this post come from the edge logs of selected production stores we operate, over a given period — about 8.5 million requests. Store names are left out on purpose.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The anti-bot series: *&lt;a href="https://www.storeframe.io/blog/magento-anti-bot-anti-scraping-guide" rel="noopener noreferrer"&gt;Part 1: Overview&lt;/a&gt;&lt;/em&gt; · &lt;em&gt;&lt;a href="https://www.storeframe.io/blog/openresty-vs-nginx-caddy-traefik-magento" rel="noopener noreferrer"&gt;Part 2: OpenResty vs NGINX, Caddy and Traefik&lt;/a&gt;&lt;/em&gt; · &lt;em&gt;&lt;a href="https://www.storeframe.io/blog/spot-fake-chrome-ja4-http2-fingerprinting" rel="noopener noreferrer"&gt;Part 3: How to Spot a Fake Chrome&lt;/a&gt;&lt;/em&gt; · &lt;em&gt;&lt;a href="https://www.storeframe.io/blog/how-to-build-a-bot-score" rel="noopener noreferrer"&gt;Part 4: How to Build a Bot Score&lt;/a&gt;&lt;/em&gt; · &lt;strong&gt;&lt;em&gt;Part 5: Self-Hosted Proof of Work with ALTCHA&lt;/em&gt;&lt;/strong&gt;.*&lt;/p&gt;

&lt;p&gt;StoreFrame runs a self-hosted, Turnstile-style proof-of-work challenge at the OpenResty edge of every Magento store, priced by what each request costs, with no third party in the path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.storeframe.io" rel="noopener noreferrer"&gt;See how StoreFrame protects Magento stores&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>magento</category>
      <category>openresty</category>
      <category>altcha</category>
      <category>captcha</category>
    </item>
    <item>
      <title>How to Build a Bot Score: 11 Signals That Separate Scrapers from Shoppers</title>
      <dc:creator>Laurentius Judhianto</dc:creator>
      <pubDate>Fri, 02 Oct 2026 14:02:06 +0000</pubDate>
      <link>https://dev.to/laurnts/how-to-build-a-bot-score-11-signals-that-separate-scrapers-from-shoppers-3d34</link>
      <guid>https://dev.to/laurnts/how-to-build-a-bot-score-11-signals-that-separate-scrapers-from-shoppers-3d34</guid>
      <description>&lt;p&gt;More than ten detectors score every request before anything is decided. Here is the scoreboard, what each signal is bad at, and the customers we annoyed on the way.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part 5 of this series will be published in the coming days. Stay tuned.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part 4 of 5 in the anti-bot series. New here? Start with Part 1, the overview: *&lt;a href="https://www.storeframe.io/blog/magento-anti-bot-anti-scraping-guide" rel="noopener noreferrer"&gt;Magento Anti-Bot and Anti-Scraping Guide&lt;/a&gt;&lt;/em&gt;.*&lt;/p&gt;

&lt;p&gt;Picture a nightclub bouncer with a clipboard. Not the one who refuses you for wearing trainers. The good one, who notices that you arrived in a taxi that does not exist, claim to be on the list under a name that belongs to someone else, and have been walking in and out of the door forty times without ever buying a drink.&lt;/p&gt;

&lt;p&gt;None of those alone gets you thrown out. All of them together, and you are going home.&lt;/p&gt;

&lt;p&gt;That is the job of the scoring layer at the edge of every Magento store we run. Every request gets looked at by more than ten independent detectors, each one adds to a score, and only then does anything get decided. This post opens up the scoreboard: what the signals are, what each one is good and bad at, and the embarrassing list of real customers we accidentally annoyed along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  First match wins was the mistake
&lt;/h2&gt;

&lt;p&gt;The first version, was a list of rules. Fourteen steps, checked in order, and the first one that matched decided what happened. Simple, fast, and completely useless when something went wrong.&lt;/p&gt;

&lt;p&gt;If a request was blocked by rule three, we never learned what rules four to fourteen thought. When a real customer got refused, the log said one reason. Usually the wrong one.&lt;/p&gt;

&lt;p&gt;Later on we rebuilt it into four phases: a few quick exits, then every detector runs and none of them stops early, then the scores are added up into a verdict, then we log and act. Every signal is recorded on every request, whether it mattered or not. That single change is why every number in this series exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scoreboard
&lt;/h2&gt;

&lt;p&gt;Here are the signals. I have left out the weights and thresholds on purpose; the categories are the interesting part anyway, and the right-hand column is the honest part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What feeds the score&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;What it catches&lt;/th&gt;
&lt;th&gt;How it gets fooled&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;User agent claims&lt;/td&gt;
&lt;td&gt;Empty user agents, self-declared libraries, browser versions that retired years ago&lt;/td&gt;
&lt;td&gt;Copy a real browser's user agent. Takes ten seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Header consistency&lt;/td&gt;
&lt;td&gt;Clients that claim to be a browser but forget what browsers always send&lt;/td&gt;
&lt;td&gt;Copy the full header set too. Takes a minute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLS fingerprint (JA4)&lt;/td&gt;
&lt;td&gt;Scripting libraries and frameworks wearing a browser name tag&lt;/td&gt;
&lt;td&gt;Browser-impersonating TLS libraries, or a real browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP/2 fingerprint&lt;/td&gt;
&lt;td&gt;The stacks that faked the TLS hello but not the conversation after it&lt;/td&gt;
&lt;td&gt;Faking the full HTTP/2 stack, or a real browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crawler verification&lt;/td&gt;
&lt;td&gt;Impostors calling themselves Googlebot or Bingbot&lt;/td&gt;
&lt;td&gt;Not really: you cannot fake Google's DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Behaviour&lt;/td&gt;
&lt;td&gt;Visitors that read page after page without loading a single image&lt;/td&gt;
&lt;td&gt;Automated real browsers load assets like anyone else&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client checks during the challenge&lt;/td&gt;
&lt;td&gt;Automation flags and inconsistencies a real browser does not have&lt;/td&gt;
&lt;td&gt;Stealth plugins, which is why this is never decisive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reputation&lt;/td&gt;
&lt;td&gt;Addresses the community has already seen misbehaving&lt;/td&gt;
&lt;td&gt;Fresh residential addresses have a clean record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request pressure&lt;/td&gt;
&lt;td&gt;Visitors hammering the store far faster than anyone shops&lt;/td&gt;
&lt;td&gt;Spread the load over thousands of addresses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold deep links&lt;/td&gt;
&lt;td&gt;First visits that land straight on a filtered or parameterised URL&lt;/td&gt;
&lt;td&gt;Easily, on its own. Which is why it is one line on the scoreboard, not a rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firewall hits&lt;/td&gt;
&lt;td&gt;Requests that carry attack payloads&lt;/td&gt;
&lt;td&gt;Nothing subtle: this one ends the conversation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read the right-hand column. Almost everything can be fooled on its own. That is the whole argument for scoring: a bot has to fool all of them, at the same time, while still looking like the same visitor. Each extra column it has to beat costs it money, time or both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four ways a request can end
&lt;/h2&gt;

&lt;p&gt;**Pass. **The page is served, nothing happens. This is where nearly every shopper lives.&lt;/p&gt;

&lt;p&gt;**Challenge. **The real page is served, and a small proof-of-work puzzle solves in the background. The shopper does not notice. A script that cannot run JavaScript never gets its pass.&lt;/p&gt;

&lt;p&gt;**Ban. **A short interstitial page that solves itself, or asks for a click on the harder cases.&lt;/p&gt;

&lt;p&gt;**Block. **A plain refusal. Reserved for the certain cases.&lt;/p&gt;

&lt;p&gt;Across multiple production stores over a given period, &lt;strong&gt;82% of requests passed untouched&lt;/strong&gt;, 3.4% were challenged invisibly, 8.2% hit the interstitial and 1.5% were blocked outright. Looking only at page requests, 19% came from a clean browser, 31% were verified search engines, and the rest was automated or suspicious. Your analytics will never tell you that, because most of those visitors never run your analytics script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Googlebot, or "Googlebot"
&lt;/h2&gt;

&lt;p&gt;Crawler verification is the one signal that is close to unfakeable, and it produced my favourite number of the whole series.&lt;/p&gt;

&lt;p&gt;For anything claiming to be a search crawler, we look up the hostname behind the connecting address, check that it belongs to the search engine, then look that hostname up again and confirm it points back to the same address. It is the method &lt;a href="https://developers.google.com/search/docs/crawling-indexing/verifying-googlebot" rel="noopener noreferrer"&gt;Google itself recommends&lt;/a&gt;. The result is cached, so it costs almost nothing.&lt;/p&gt;

&lt;p&gt;Over the same period, &lt;strong&gt;210 of the 408 addresses that called themselves Googlebot were fake&lt;/strong&gt;. Bing, by comparison, had 3 impostors out of 289. Apparently nobody dresses up as Bing. The fakes were refused 97% of the time; the real Google sent 98% of the Googlebot traffic and was never challenged once, because a store that blocks Google has solved the bot problem in the least useful way possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hall of shame
&lt;/h2&gt;

&lt;p&gt;Every signal in that table has, at some point, fired on someone it should not have. Here is the list, because pretending otherwise would be the least credible thing in this post.&lt;/p&gt;

&lt;p&gt;**Our own dashboard. **Banned by our own edge, the same week the scoring went live. Clean work.&lt;/p&gt;

&lt;p&gt;**Home internet on IPv6. **Some residential providers give their customers reverse-DNS names that looked suspicious to the crawler check. Real people, flagged for their ISP's naming taste.&lt;/p&gt;

&lt;p&gt;**Facebook link previews. **Somebody shares a product, Facebook fetches it, we refuse Facebook. Not ideal for a shop.&lt;/p&gt;

&lt;p&gt;**Google Ads and newsletter clicks. **They arrive with long tracking parameters, which looked a lot like a bot guessing URLs. We relaxed that signal. Scrapers noticed and started dressing up as ad clicks. We tightened it again, more carefully.&lt;/p&gt;

&lt;p&gt;**Our own colleagues. **Team members on certain ISP or VPN, where a lot of home traffic looks like data centre traffic, got throttled. We changed how much that signal is allowed to count.&lt;/p&gt;

&lt;p&gt;Every one of these was caught because every signal is logged, and fixed by adjusting one weight rather than tearing out a layer. That is the other benefit of scoring: mistakes are tunable, not fatal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just block by address?
&lt;/h2&gt;

&lt;p&gt;Because addresses have stopped meaning anything. On a day in September, one store saw 32,462 refused requests in one hour from 31,201 different addresses. Roughly one request each. A per-address rule sees thirty-one thousand polite strangers.&lt;/p&gt;

&lt;p&gt;Many of those addresses are residential proxies: real home and mobile connections rented out to scrapers. Blocking them outright also blocks the shopper who happens to share that connection tomorrow. So the score has to judge the visit, not the address, and the next part of this series, about proof of work, is how we make those visits expensive instead of trying to blacklist the planet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally, measuring it and constantly update algorithm
&lt;/h2&gt;

&lt;p&gt;An anti-bot layer you cannot see is a rumour. Every decision the edge makes lands in a central log store, off the store itself, with every signal attached.&lt;/p&gt;

&lt;p&gt;None of it is perfect, and none of it has to be, I don't think it ever will. The score does not need to be right about every visitor. &lt;strong&gt;It needs to be right about enough of them, cheaply, while the other layers catch what it misses&lt;/strong&gt;. Finding the right balance between acessibility and security.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Numbers in this post come from the edge logs of selected production stores we operate, over a given period — about 8.5 million requests. Store names are left out on purpose.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next up, Part 5: Self-Hosted Proof of Work with ALTCHA (coming soon).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The anti-bot series: *&lt;a href="https://www.storeframe.io/blog/magento-anti-bot-anti-scraping-guide" rel="noopener noreferrer"&gt;Part 1: Overview&lt;/a&gt;&lt;/em&gt; · &lt;em&gt;&lt;a href="https://www.storeframe.io/blog/openresty-vs-nginx-caddy-traefik-magento" rel="noopener noreferrer"&gt;Part 2: OpenResty vs NGINX, Caddy and Traefik&lt;/a&gt;&lt;/em&gt; · &lt;em&gt;&lt;a href="https://www.storeframe.io/blog/spot-fake-chrome-ja4-http2-fingerprinting" rel="noopener noreferrer"&gt;Part 3: How to Spot a Fake Chrome&lt;/a&gt;&lt;/em&gt; · &lt;strong&gt;&lt;em&gt;Part 4: How to Build a Bot Score&lt;/em&gt;&lt;/strong&gt; · &lt;em&gt;Part 5: Self-Hosted Proof of Work with ALTCHA (coming soon)&lt;/em&gt;.*&lt;/p&gt;

&lt;p&gt;StoreFrame scores every request at an OpenResty edge in front of every Magento store it runs: fingerprints, crawler verification, behaviour and reputation, with every decision logged centrally.&lt;br&gt;
&lt;a href="https://www.storeframe.io" rel="noopener noreferrer"&gt;See how StoreFrame protects Magento stores&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>magento</category>
      <category>openresty</category>
      <category>bots</category>
    </item>
    <item>
      <title>How to Spot a Fake Chrome: JA4 and HTTP/2 Fingerprinting Explained</title>
      <dc:creator>Laurentius Judhianto</dc:creator>
      <pubDate>Mon, 28 Sep 2026 09:53:42 +0000</pubDate>
      <link>https://dev.to/laurnts/how-to-spot-a-fake-chrome-ja4-and-http2-fingerprinting-explained-334e</link>
      <guid>https://dev.to/laurnts/how-to-spot-a-fake-chrome-ja4-and-http2-fingerprinting-explained-334e</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdfkma85iao9tj3xvumd5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdfkma85iao9tj3xvumd5.png" width="800" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every bot says it is Chrome. The handshake says otherwise. How we read JA4 and HTTP/2 fingerprints at the edge, why we needed both, and where they fail.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Parts 4 and 5 of this series will be published over the coming days. Stay tuned.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part 3 of 5 in the anti-bot series. New here? Start with Part 1, the overview: *&lt;a href="https://www.storeframe.io/blog/magento-anti-bot-anti-scraping-guide" rel="noopener noreferrer"&gt;Magento Anti-Bot and Anti-Scraping Guide&lt;/a&gt;&lt;/em&gt;.*&lt;/p&gt;

&lt;p&gt;Every bot on the internet is Chrome (webkit, sort-of). Just ask it. Chrome on Windows, the latest version, with all the right headers, freshly copied from a real browser by someone who read a blog post much like this one.&lt;/p&gt;

&lt;p&gt;The user agent is a name tag. Anyone can write anything on a name tag (a curl posted with a handwritten UA can bypass Cloudflare, maybe the free one, yet it did). What is much harder to fake is the way a client actually talks: how it negotiates encryption, and how it opens an &lt;a href="https://www.rfc-editor.org/rfc/rfc9113" rel="noopener noreferrer"&gt;HTTP/2&lt;/a&gt; connection. Those habits live deep inside the network library, and most scraper authors never touch them.&lt;/p&gt;

&lt;p&gt;This post is about reading those habits on every request at the edge of a Magento store: what JA4 and HTTP/2 fingerprints are, why we needed both, the afternoon I spent patching &lt;a href="https://openresty.org/" rel="noopener noreferrer"&gt;OpenResty&lt;/a&gt; from source, and the day fingerprints let us down.&lt;/p&gt;

&lt;p&gt;Great if you haven't heard of it!&lt;/p&gt;

&lt;h2&gt;
  
  
  JA4: the handshake as handwriting
&lt;/h2&gt;

&lt;p&gt;Before any page is requested, the client says hello to the server and lists what it supports: which versions of TLS, which ciphers, which extensions, which protocols it would like to speak next. Chrome says hello one way. Firefox another. Python's requests library, Go's HTTP client and &lt;a href="https://curl.se/" rel="noopener noreferrer"&gt;curl&lt;/a&gt; each have their own accent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/FoxIO-LLC/ja4" rel="noopener noreferrer"&gt;JA4&lt;/a&gt; is an open standard from FoxIO, launched in 2023, that turns that hello into a short fingerprint. It replaced the older &lt;a href="https://github.com/salesforce/ja3" rel="noopener noreferrer"&gt;JA3&lt;/a&gt; for a very practical reason, as &lt;a href="https://blog.cloudflare.com/ja4-signals/" rel="noopener noreferrer"&gt;Cloudflare explains&lt;/a&gt;: in 2023 Chrome started shuffling the order of its extensions on every connection, which made JA3 fingerprints change constantly. JA4 sorts them first, so the shuffle stops mattering.&lt;/p&gt;

&lt;p&gt;The trick is not identifying the browser. We do not care whether you are on Chrome or Firefox. We care whether the handshake belongs to a browser at all, when the name tag says it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP/2: the layer they forget
&lt;/h2&gt;

&lt;p&gt;Here is the problem with TLS fingerprints on their own: there are libraries whose whole job is to fake a browser's hello, byte for byte. Good ones. Scrapers use them.&lt;/p&gt;

&lt;p&gt;But after the hello, if the connection speaks HTTP/2, the client sends its opening settings: window sizes, limits, and the order in which it lists certain headers. Every browser engine has its own habits here too, and they come from the HTTP stack, not the TLS library. Faking the handshake does not fake these. You have to fake the whole conversation, consistently.&lt;/p&gt;

&lt;p&gt;Stock &lt;a href="https://nginx.org/" rel="noopener noreferrer"&gt;NGINX&lt;/a&gt; does not expose any of this. Neither does OpenResty. So we took the HTTP/2 part of an open-source fingerprinting patch (&lt;a href="https://github.com/phuslu/nginx-ssl-fingerprint" rel="noopener noreferrer"&gt;nginx-ssl-fingerprint&lt;/a&gt;), stripped out everything else, and compiled it into the NGINX core inside OpenResty. That meant giving up prebuilt images and building our own, from source, in stages.&lt;/p&gt;

&lt;p&gt;The first version worked perfectly, in the sense that it compiled, started, and returned an empty fingerprint for every single request. A missing flag. Two days later it was reading real values.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenResty: the edge
&lt;/h2&gt;

&lt;p&gt;Now the evidence. Across multiple production stores over a given period, &lt;strong&gt;174,920 requests that claimed to be a browser had the connection of something else&lt;/strong&gt;: 3.4% of all browser-claiming traffic, from 18,325 addresses.&lt;/p&gt;

&lt;p&gt;The TLS fingerprint caught most of them: about 129,000 had handshake traits no current browser sends, and another 41,000 matched signatures of known scraping frameworks. The HTTP/2 fingerprint caught another 4,136 that the handshake missed: Python, curl and Java stacks, all wearing a Chrome name tag.&lt;/p&gt;

&lt;p&gt;And the number that justifies building both: &lt;strong&gt;only 86 requests were caught by both fingerprints at once&lt;/strong&gt;. They overlap almost nowhere. Each catches what the other misses. Remove either one and a whole category walks in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where fingerprints fail
&lt;/h2&gt;

&lt;p&gt;I promised trade-offs, so here they are. Fingerprints catch fakes. They do not catch the real thing.&lt;/p&gt;

&lt;p&gt;**A real browser is a real browser. **In one period, a swarm hit one of our stores running genuine Chrome, driven by automation. Its fingerprint was identical to that of 465 real customers' addresses on the same store. At the time we had a wall that armed itself on fingerprints when the store was under load. It did exactly what it was told: it turned away real shoppers, and it broke checkout. We removed it shortly for good. Keying on identity is a dead end when the identity is shared with your customers.&lt;/p&gt;

&lt;p&gt;**The network can rewrite your handshake. **A few months back, one of our own team was on an a certain ISP and got refused by our own edge. The carrier was intercepting TLS, so a perfectly normal phone arrived with a handshake that did not look like a phone. Corporate proxies do the same thing. Your customers do not choose their networks.&lt;/p&gt;

&lt;p&gt;That is exactly Cloudflare's point: fingerprints can be easily spoofed, they change frequently, and traffic keeps evolving. So on the pages shoppers browse, a fingerprint mismatch is never a verdict on its own. It is evidence. It feeds the score, along with everything else, and the score decides. In our data, some mismatched requests still pass, because nothing else about them looks wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one place a mismatch is a verdict
&lt;/h2&gt;

&lt;p&gt;Stores also have machine-facing channels, where payment providers, marketplaces and stock systems talk to Magento. Honest software on those channels introduces itself honestly. It has no reason to pretend to be Chrome.&lt;/p&gt;

&lt;p&gt;So when a client on a machine channel claims to be a browser and its connection says otherwise, there is only one explanation left (malicious). N a price scraper went after a store's product catalogue through its machine channel, dressed as Chrome. Since then that combination is refused outright.&lt;/p&gt;

&lt;p&gt;Fingerprints are not a wall. They are one of the layers that makes lying expensive. Fake ones are definitely goes to the bin with difficult proof of work. The next part is about how all the layers add up to a decision.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Numbers in this post come from the edge logs of selected production stores we operate, over a given period — about 8.5 million requests. Store names are left out on purpose.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next up, Part 4: How to Build a Bot Score (coming soon).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The anti-bot series: *&lt;a href="https://www.storeframe.io/blog/magento-anti-bot-anti-scraping-guide" rel="noopener noreferrer"&gt;Part 1: Overview&lt;/a&gt;&lt;/em&gt; · &lt;em&gt;&lt;a href="https://www.storeframe.io/blog/openresty-vs-nginx-caddy-traefik-magento" rel="noopener noreferrer"&gt;Part 2: OpenResty vs NGINX, Caddy and Traefik&lt;/a&gt;&lt;/em&gt; · &lt;strong&gt;&lt;em&gt;Part 3: How to Spot a Fake Chrome&lt;/em&gt;&lt;/strong&gt; · &lt;em&gt;Part 4: How to Build a Bot Score (coming soon)&lt;/em&gt; · &lt;em&gt;Part 5: Self-Hosted Proof of Work with ALTCHA (coming soon)&lt;/em&gt;.*&lt;/p&gt;


&lt;p&gt;StoreFrame reads the TLS and HTTP/2 fingerprint of every request at an OpenResty edge in front of every Magento store it runs, alongside scoring, proof of work and a firewall. No third party in the path.&lt;/p&gt;
&lt;a href="https://www.storeframe.io" rel="noopener noreferrer"&gt;See how StoreFrame protects Magento stores&lt;/a&gt;

</description>
      <category>cybersecurity</category>
      <category>magento</category>
      <category>fingerprinting</category>
      <category>ja4</category>
    </item>
    <item>
      <title>OpenResty vs NGINX, Caddy and Traefik: Choosing The Edge For Magento</title>
      <dc:creator>Laurentius Judhianto</dc:creator>
      <pubDate>Thu, 24 Sep 2026 09:45:42 +0000</pubDate>
      <link>https://dev.to/laurnts/openresty-vs-nginx-caddy-and-traefik-choosing-the-edge-for-magento-3pn2</link>
      <guid>https://dev.to/laurnts/openresty-vs-nginx-caddy-and-traefik-choosing-the-edge-for-magento-3pn2</guid>
      <description>&lt;p&gt;There are a dozen ways to put a web server in front of Magento. I tried most of them and landed on OpenResty — NGINX with Lua superpowers. Here's the honest case for it, and against the alternatives.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Parts 3 to 5 of this series will be published over the coming days. Stay tuned.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part 2 of 5 in the anti-bot series. New here? Start with Part 1, the overview: *&lt;a href="https://www.storeframe.io/blog/magento-anti-bot-anti-scraping-guide" rel="noopener noreferrer"&gt;Magento Anti-Bot and Anti-Scraping Guide&lt;/a&gt;&lt;/em&gt;.*&lt;/p&gt;

&lt;p&gt;Pick the web server that sits in front of Magento and you’ve quietly made one of the most consequential decisions in the whole stack. It’s the first thing every request touches and the last thing every response leaves through. I tried a lot of front doors over the years. I landed on &lt;a href="https://openresty.org/" rel="noopener noreferrer"&gt;OpenResty&lt;/a&gt;, and I’ll defend it — but only after being fair to everything it beat.&lt;/p&gt;

&lt;p&gt;The one-line version: OpenResty is &lt;a href="https://nginx.org/" rel="noopener noreferrer"&gt;NGINX&lt;/a&gt; with &lt;a href="https://www.lua.org/" rel="noopener noreferrer"&gt;Lua&lt;/a&gt; superpowers. Everything good about NGINX as a Magento front door, plus the ability to run real logic at the edge — which, it turns out, is exactly what a modern Magento store needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plain NGINX: right model, not enough rope
&lt;/h2&gt;

&lt;p&gt;Let’s start with the obvious one, because it’s the baseline everyone knows. For a Magento front door you want NGINX, full stop — its event-driven model is the right shape for this workload in a way &lt;a href="https://httpd.apache.org/" rel="noopener noreferrer"&gt;Apache&lt;/a&gt;’s per-request processes simply aren’t. Magento even ships an NGINX config. This isn’t a religious war anymore.&lt;/p&gt;

&lt;p&gt;So why not just run stock NGINX? Because the moment you want to do anything genuinely smart at the edge — a proof-of-work challenge for suspicious traffic, custom WAF logic, talking to a security bouncer, shaping requests based on real conditions — plain NGINX hands you static directives and a polite shrug. You can bolt on modules, but you’re recompiling and you’re still not programming. I needed to run logic at the edge, per request, and stock NGINX doesn’t give you the rope. OpenResty is that same NGINX, with Lua wired into every phase of the request. Same foundation, suddenly programmable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caddy: lovely, but it can’t stand where I need it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://caddyserver.com/" rel="noopener noreferrer"&gt;Caddy&lt;/a&gt; is genuinely nice — automatic HTTPS, clean config, a pleasure for a lot of jobs. And it keeps coming up because &lt;a href="https://frankenphp.dev/" rel="noopener noreferrer"&gt;FrankenPHP&lt;/a&gt;, the shiny &lt;a href="https://www.php.net/" rel="noopener noreferrer"&gt;PHP&lt;/a&gt; runtime everyone’s excited about, is built on Caddy. I tried that road. The wall I hit is practical: FrankenPHP wants Caddy, and I can’t run Caddy as the front door without throwing away full compatibility with the standard Magento NGINX config — and that compatibility matters far more than it sounds. The Magento NGINX config is a known quantity, battle-tested, and everything from upload handling to static-file rules assumes it. Swapping the whole front door to chase an unproven gain isn’t a trade I’ll make. Caddy’s a great tool standing in the wrong doorway for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traefik: a brilliant router for the wrong problem
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://traefik.io/traefik/" rel="noopener noreferrer"&gt;Traefik&lt;/a&gt; is excellent at what it’s for — dynamic service discovery, routing across a fleet of containers, the cloud-native ingress problem. I actually run it elsewhere for exactly that. But a Magento store’s front door isn’t a routing problem; it’s a single, opinionated, high-performance edge that has to do Magento-specific things and run custom logic on the hot path. Traefik is the wrong altitude — a smart traffic cop when what I need is a tuned, programmable wall. Different job.&lt;/p&gt;

&lt;h2&gt;
  
  
  APISIX: powerful, and quietly OpenResty anyway
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apisix.apache.org/" rel="noopener noreferrer"&gt;APISIX&lt;/a&gt; is a serious API gateway, and here’s the punchline: it’s itself built on top of OpenResty. So when I evaluated it, I was really evaluating “OpenResty with a large API-gateway framework wrapped around it.” For an API-management problem — hundreds of routes, plugins, a control plane — that framework earns its weight. For a Magento front door it’s a lot of machinery I’d have to learn, configure, and keep updated to get to a place I can reach by using the engine underneath it directly. Why take the wrapper when I want the thing it wraps?&lt;/p&gt;

&lt;h2&gt;
  
  
  Angie and Tengine: the NGINX forks
&lt;/h2&gt;

&lt;p&gt;Then the NGINX forks, because someone always asks. &lt;a href="https://angie.software/en/" rel="noopener noreferrer"&gt;Angie&lt;/a&gt; is a newer fork from ex-nginx developers — genuinely promising, some nice features — but it’s younger, the ecosystem is thinner, and I’m not ready to bet a fleet of customer stores on it yet. Maybe later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tengine.taobao.org/" rel="noopener noreferrer"&gt;Tengine&lt;/a&gt; — Alibaba’s NGINX fork — I actually went further with. I was curious enough to try building it from the makefile myself. The result: slow going, and ultimately nowhere sensible to run it that justified the effort over what I already had. It’s clearly capable at Alibaba’s scale, but for my workload it was friction without a payoff I could measure. I gave up and went back to the thing that was already working.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Lua superpowers actually buy me
&lt;/h2&gt;

&lt;p&gt;So why did OpenResty win? Because it let me build the exact edge Magento needs instead of approximating it with whatever directives a config language happened to expose. Let me be specific about what runs at that front door, because ‘NGINX with Lua’ is abstract until you see the list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/google/brotli" rel="noopener noreferrer"&gt;Brotli&lt;/a&gt; compression, so the bytes on the wire are smaller than gzip gets you — free speed on every text response. HTTP/3 and QUIC, so connections set up faster and survive a flaky mobile network without stalling, which matters more for real shoppers on real phones than any synthetic benchmark admits. &lt;a href="https://www.maxmind.com/en/geoip-databases" rel="noopener noreferrer"&gt;GeoIP&lt;/a&gt;, so I can reason about where traffic is coming from. A &lt;a href="https://www.crowdsec.net/" rel="noopener noreferrer"&gt;CrowdSec&lt;/a&gt; bouncer, so known-bad actors are turned away at the door based on a shared, constantly-updated reputation feed rather than a static blocklist I have to maintain by hand. And AppSec rules running inline, inspecting requests for the obvious attack shapes before they ever reach PHP.&lt;/p&gt;

&lt;p&gt;And then the one I’m actually proudest of, the one that only exists because Lua let me write real logic in the request path: a proof-of-work challenge for suspicious traffic. When a request looks like a scanner or a scraper rather than a human, the edge can make it do a small computational puzzle before it’s allowed through — cheap and invisible for a real browser, expensive and discouraging at the scale a bot operates. It’s the same idea as the challenge pages you’ve hit on big sites, except it’s mine, running locally, instead of routing every visitor through a third party and paying for it in latency, money, and an extra network hop. I could have taken the easy road and bolted on someone else’s challenge service. It was uglier, slower, and cost money on every request, and my R&amp;amp;D nerves wouldn’t allow it. So I built it. That’s a thing you can only do when your edge is programmable.&lt;/p&gt;

&lt;p&gt;Here’s why that last category matters so much for Magento specifically, and it ties back to performance. A scanner swarm doesn’t hammer your cached homepage — it hits the pages caching can’t save you on: search, layered-navigation permutations, the store switcher, add-to-cart and checkout paths. Every one of those sails past &lt;a href="https://varnish-cache.org/" rel="noopener noreferrer"&gt;Varnish&lt;/a&gt;, wakes a PHP-FPM worker, and ties up a process a real customer needed. So shaping that traffic at the edge isn’t a security nicety bolted on after the fact. It’s a performance feature. It reclaims the workers your shoppers are supposed to be using. And it lives at the edge because the edge is the only place to stop it before it costs you anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The constraint that quietly disqualified half the field
&lt;/h2&gt;

&lt;p&gt;Through every bit of that — Brotli, HTTP/3, GeoIP, CrowdSec, AppSec, the proof-of-work challenge — OpenResty keeps full compatibility with the standard Magento NGINX config. That sentence sounds boring and it’s the most important one in the post. The Magento NGINX config is a known, battle-tested quantity; the upload rules, the static-file handling, the security paths, the things that route to PHP correctly are all spelled out in it and assumed by every deploy. OpenResty runs that config unchanged and adds the programmable layer around it. I get the power without throwing away the foundation.&lt;/p&gt;

&lt;p&gt;That’s the trade nothing else on the list could make. Plain NGINX kept the config but couldn’t program. Caddy could do clever things but couldn’t keep the Magento config and forced a whole runtime change to get there. Traefik was solving a routing problem I didn’t have. APISIX was the long way round to the engine I could just use directly. The forks were either too young to bet a fleet on or not worth the effort once I’d actually tried them. OpenResty was the only one that gave me programmability and the known-good Magento foundation at the same time — and once you frame the requirement that way, it almost picks itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The front-door decision, in one table&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;OpenResty (what I run)&lt;/th&gt;
&lt;th&gt;The alternatives&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run logic at the edge&lt;/td&gt;
&lt;td&gt;Yes — Lua in every request phase&lt;/td&gt;
&lt;td&gt;Plain NGINX: no. Caddy: limited. Traefik: routing only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keep stock Magento NGINX config&lt;/td&gt;
&lt;td&gt;Yes, unchanged&lt;/td&gt;
&lt;td&gt;Caddy: no (runtime change). Forks: mostly, but unproven&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Own proof-of-work / WAF logic&lt;/td&gt;
&lt;td&gt;Yes, self-hosted, no extra hop&lt;/td&gt;
&lt;td&gt;Usually means a third-party service + latency + cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Right job for a Magento front door&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Traefik/APISIX: built for routing / API gateways&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maturity to bet a fleet on&lt;/td&gt;
&lt;td&gt;Yes — years in production&lt;/td&gt;
&lt;td&gt;Angie: young. Tengine: friction, no payoff for me&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Boring foundation, power where it counts
&lt;/h2&gt;

&lt;p&gt;None of this is exotic for its own sake — and if you’ve read anything else I’ve written you know that’s the through-line of everything I build. Boring, known-good foundation; just enough power applied exactly where the workload actually demands it, and nowhere it doesn’t. OpenResty is NGINX you can reason with and program, and a Magento front door — the first thing every request touches, the place performance and security are both won or lost — is precisely the spot where that reasoning pays for itself a hundred times over.&lt;/p&gt;

&lt;p&gt;If you want the deeper dive on why the edge matters so much for raw performance — the scanner-bot problem especially — I went into it at length in my Magento performance post. This post was the front-door decision. The rest of this series is what the front door actually does with all that Lua: how we fingerprint connections (including the part where I patched OpenResty from source to read &lt;a href="https://www.rfc-editor.org/rfc/rfc9113" rel="noopener noreferrer"&gt;HTTP/2&lt;/a&gt;), how we score every request, and the proof-of-work challenge I ended up building three times, because apparently I enjoy pain.&lt;/p&gt;

&lt;p&gt;Next up, Part 3: How to Spot a Fake Chrome (coming soon).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The anti-bot series: *&lt;a href="https://www.storeframe.io/blog/magento-anti-bot-anti-scraping-guide" rel="noopener noreferrer"&gt;Part 1: Overview&lt;/a&gt;&lt;/em&gt; · &lt;strong&gt;&lt;em&gt;Part 2: OpenResty vs NGINX, Caddy and Traefik&lt;/em&gt;&lt;/strong&gt; · &lt;em&gt;Part 3: How to Spot a Fake Chrome (coming soon)&lt;/em&gt; · &lt;em&gt;Part 4: How to Build a Bot Score (coming soon)&lt;/em&gt; · &lt;em&gt;Part 5: Self-Hosted Proof of Work with ALTCHA (coming soon)&lt;/em&gt;.*&lt;/p&gt;

&lt;p&gt;The edge is where a lot of Magento performance and security is won or lost. StoreFrame runs a tuned, programmable OpenResty front door on every store — yours included.&lt;br&gt;
&lt;a href="https://www.storeframe.io" rel="noopener noreferrer"&gt;See how StoreFrame works&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openresty</category>
      <category>nginx</category>
      <category>magento</category>
      <category>performance</category>
    </item>
    <item>
      <title>Magento Anti-Bot and Anti-Scraping: A Layered Defence Guide for Self-Hosted Stores</title>
      <dc:creator>Laurentius Judhianto</dc:creator>
      <pubDate>Wed, 23 Sep 2026 13:15:16 +0000</pubDate>
      <link>https://dev.to/laurnts/magento-anti-bot-and-anti-scraping-a-layered-defence-guide-for-self-hosted-stores-1lpi</link>
      <guid>https://dev.to/laurnts/magento-anti-bot-and-anti-scraping-a-layered-defence-guide-for-self-hosted-stores-1lpi</guid>
      <description>&lt;p&gt;Half of a Magento store's traffic are bots, and today's scrapers solve puzzles and rent home internet. Here is the layered strategy we run, with real numbers.&lt;/p&gt;

&lt;p&gt;Every Magento store I run has more visitors that are bots than human. I used to say that as a joke. Then I counted.&lt;/p&gt;

&lt;p&gt;Across multiple production stores over a given period, only &lt;strong&gt;19%&lt;/strong&gt; of page requests came from a clean, ordinary browser. Another 31% were verified search engines, which we want. The remaining half was everything else: price scrapers, stock checkers, SEO tools, AI crawlers hoovering up product copy, credential stuffers, and a steady drizzle of vulnerability scanners.&lt;/p&gt;

&lt;p&gt;Most of it is not an attack. All of it costs money. A bot walking your layered navigation burns the same &lt;a href="https://www.php.net/" rel="noopener noreferrer"&gt;PHP&lt;/a&gt; and database time as a shopper, and the shopper is the only one who ever pays you back.&lt;/p&gt;

&lt;p&gt;On a managed platform with a giant CDN in front, somebody else fights this for you. Self-hosted, congratulations: you are the CDN. That is why I think anti-bot is the hardest part of running Magento yourself. Harder than caching. Harder than upgrades. And constantly eating server resources.&lt;/p&gt;

&lt;p&gt;This post is the overview of how we handle it on every store we operate. Each layer gets a short section, and four follow-up posts go deeper. I will explain how each piece works. I will not publish the numbers, weights or exceptions, for reasons that become obvious by the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scrapers in 2026 are not your cousin's curl script
&lt;/h2&gt;

&lt;p&gt;Let me set expectations, because the internet still thinks a bot is a Python script with a funny user agent. Some are. The ones that matter are not.&lt;/p&gt;

&lt;p&gt;**They solve proof of work. **One swarm that hit us solved our challenge every single time it was asked: 2,490 challenges, 2,490 solutions. Politely. Instantly.&lt;/p&gt;

&lt;p&gt;**They drive real browsers. **Detecting headless Chrome was a fun hobby in 2020. In February 2026 we added headless detection to our challenge page and removed it the same evening, because stealth plugins walked straight past every check. A real browser, driven by automation, is still a real browser.&lt;/p&gt;

&lt;p&gt;**They rent your customers' internet. **Residential proxy networks route scraper traffic through real home and mobile connections. A scraper on one of those looks exactly like a shopper on a VPN, or on a mobile carrier doing something creative with its traffic. Block the address and you block the buyer.&lt;/p&gt;

&lt;p&gt;**They spread thin. **One hour on a day in September, one store: 32,462 requests from 31,201 different addresses. That is roughly one visit per address, then gone. Any rule that counts requests per address sees 31,201 perfectly polite visitors.&lt;/p&gt;

&lt;p&gt;**They lie about who they are. **Most of them claim to be Chrome. Many claim to be Googlebot. The claiming is cheap; making everything else agree with the claim is not, which is where most of our catching happens.&lt;/p&gt;

&lt;p&gt;**And fingerprints have flaws too. **In August, a swarm ran genuine Chrome. Its fingerprint was shared with 465 addresses of real customers on the same store. We had a wall that armed itself on fingerprints when a store was under load. That day it turned away real shoppers and broke checkout. We took it down for good. Lesson paid for in full.&lt;/p&gt;

&lt;h2&gt;
  
  
  There is no one thing
&lt;/h2&gt;

&lt;p&gt;If you have read &lt;a href="https://www.zdnet.com/article/fed-up-with-ai-scraping-your-content-this-open-source-bot-blocker-can-help-heres-how/" rel="noopener noreferrer"&gt;ZDNet's piece on Anubis&lt;/a&gt;, the open-source bot blocker that makes every visitor solve a proof-of-work puzzle, you know the idea is good. It is the same family of idea we use. Its own README calls it &lt;a href="https://github.com/TecharoHQ/anubis" rel="noopener noreferrer"&gt;a bit of a nuclear response&lt;/a&gt;, and it is honest about why.&lt;/p&gt;

&lt;p&gt;Then, in August 2025, &lt;a href="https://www.theregister.com/software/2025/08/16/codeberg-beset-by-ai-bots-that-now-bypass-anubis-defense/1023101" rel="noopener noreferrer"&gt;Codeberg reported that AI crawlers had learned to solve the Anubis challenges&lt;/a&gt;. Not because Anubis is bad: any single mechanism, once popular, gets optimised against.&lt;/p&gt;

&lt;p&gt;Cloudflare says the same thing about fingerprints in &lt;a href="https://blog.cloudflare.com/ja4-signals/" rel="noopener noreferrer"&gt;its JA4 signals write-up&lt;/a&gt;: fingerprints can be easily spoofed, they change frequently, and traffic keeps evolving. Their answer is to combine them with other signals. So is ours.&lt;/p&gt;

&lt;p&gt;So the strategy is not a wall. It is a lot of small, cheap questions, each of which a bot can pass, and very few bots can pass all of them at once while still looking like the same visitor.&lt;/p&gt;

&lt;h2&gt;
  
  
  The edge: OpenResty instead of plain NGINX
&lt;/h2&gt;

&lt;p&gt;Every store sits behind &lt;a href="https://openresty.org/" rel="noopener noreferrer"&gt;OpenResty&lt;/a&gt;: &lt;a href="https://nginx.org/" rel="noopener noreferrer"&gt;NGINX&lt;/a&gt; with a &lt;a href="https://www.lua.org/" rel="noopener noreferrer"&gt;Lua&lt;/a&gt; runtime inside, so every stage of a request, from the encryption handshake to the log line, can run our own logic before a single line of PHP wakes up.&lt;/p&gt;

&lt;p&gt;Plain NGINX can match paths and count requests. It cannot inspect a connection, keep a score, or talk to a challenge service. Part 1 covers why OpenResty and not &lt;a href="https://caddyserver.com/" rel="noopener noreferrer"&gt;Caddy&lt;/a&gt;, &lt;a href="https://traefik.io/traefik/" rel="noopener noreferrer"&gt;Traefik&lt;/a&gt;, &lt;a href="https://apisix.apache.org/" rel="noopener noreferrer"&gt;APISIX&lt;/a&gt; or the forks: &lt;a href="https://dev.to/blog/openresty-vs-nginx-caddy-traefik-magento"&gt;OpenResty vs NGINX, Caddy and Traefik&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detect first, decide later
&lt;/h2&gt;

&lt;p&gt;The most important design rule is boring: no single signal is allowed to block anyone.&lt;/p&gt;

&lt;p&gt;Every request runs through every detector, and each one only adds to a score. Only when all of them are done does the edge look at the total. Low score, the visitor passes untouched. Middling, a challenge runs in the background. High, they are refused.&lt;/p&gt;

&lt;p&gt;In our week of data, 82% of requests passed untouched, 3.4% got an invisible challenge, and 9.7% were stopped. Part 3 opens up the scoreboard: &lt;a href="https://dev.to/blog/how-to-build-a-bot-score"&gt;How to Build a Bot Score&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  JA4 and HTTP/2: fingerprinting the connection
&lt;/h2&gt;

&lt;p&gt;Every bot claims to be Chrome, and many copy Chrome's headers perfectly. What they rarely copy is how Chrome connects. The encryption handshake is as distinctive as handwriting, and &lt;a href="https://github.com/FoxIO-LLC/ja4" rel="noopener noreferrer"&gt;JA4&lt;/a&gt; turns it into a short fingerprint. &lt;a href="https://www.rfc-editor.org/rfc/rfc9113" rel="noopener noreferrer"&gt;HTTP/2&lt;/a&gt; adds a second one: the settings a client announces when it opens the connection, baked deep into its network stack. Stock NGINX exposes neither, so we patched OpenResty from source to read them.&lt;/p&gt;

&lt;p&gt;We do not care which browser you use. We care whether your connection matches the browser you claim to be. In our week of data, 174,920 requests claiming to be a browser had the connection of something else. Only 86 of them were caught by both fingerprints. Each caught what the other missed. That is layering in one number.&lt;/p&gt;

&lt;p&gt;Part 2 goes deep on this, including the bugs: &lt;a href="https://dev.to/blog/spot-fake-chrome-ja4-http2-fingerprinting"&gt;How to Spot a Fake Chrome&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse DNS against the fake Googlebot
&lt;/h2&gt;

&lt;p&gt;The oldest trick in the book: call yourself Googlebot. Stores are terrified of blocking Google, so plenty of setups wave that name straight through.&lt;/p&gt;

&lt;p&gt;Google &lt;a href="https://developers.google.com/search/docs/crawling-indexing/verifying-googlebot" rel="noopener noreferrer"&gt;publishes how to verify its crawler&lt;/a&gt;: look up the hostname behind the address, check it is Google's, then confirm that hostname points back to the same address. We do that for anything claiming to be a search crawler.&lt;/p&gt;

&lt;p&gt;The result made me laugh out loud: &lt;strong&gt;210 of the 408 addresses claiming to be Googlebot were fake&lt;/strong&gt;. The real Googlebot is never challenged, because we want to be indexed. The impostors were blocked 97% of the time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Behaviour and reputation
&lt;/h2&gt;

&lt;p&gt;A real browser viewing a product page also downloads its images. A scraper that only wants the HTML usually does not. Page after page with no pictures feeds the score quietly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.crowdsec.net" rel="noopener noreferrer"&gt;CrowdSec&lt;/a&gt; adds memory: a community blocklist of about 22,000 to 25,000 addresses per store, dropped at the firewall before they reach the edge. Addresses that merely behaved oddly get a score bump instead of a ban, because a shared office network should not be punished for one noisy laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Turnstile strategy, without the Cloudflare
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.cloudflare.com/application-services/products/turnstile/" rel="noopener noreferrer"&gt;Cloudflare Turnstile&lt;/a&gt; changed how people think about captchas: most visitors should never see a puzzle. The browser proves itself quietly in the background, and only the doubtful ones see anything.&lt;/p&gt;

&lt;p&gt;We loved the strategy and did not want the dependency: third-party JavaScript on every page, a vendor in the checkout path, visitor data leaving the store. So we built the same pattern ourselves, self-hosted, and it took three attempts to get it right.&lt;/p&gt;

&lt;p&gt;There is a boring reason too, and it might be the best one: time. Turnstile is set up per site, by a person: an account, keys, a widget wired into the store, domains to register. Ours ships with the store. It is installed automatically when a store is first provisioned, so every store has the same protection from day one, and nobody has to remember to go and set up a captcha. Part 4 is the story: &lt;a href="https://dev.to/blog/altcha-proof-of-work-turnstile-alternative"&gt;Self-Hosted Proof of Work with ALTCHA&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proof of work and ALTCHA
&lt;/h2&gt;

&lt;p&gt;What the browser solves is a proof-of-work puzzle: search for a number that makes a deliberately slow hash come out right, which the server checks in one go. For one shopper that is a moment of CPU they never notice. For a scraper opening thousands of fresh sessions an hour, it is a compute bill, every session, forever. Proof of work does not prove you are human. It makes being a bot at scale expensive.&lt;/p&gt;

&lt;p&gt;The engine is &lt;a href="https://altcha.org" rel="noopener noreferrer"&gt;ALTCHA&lt;/a&gt;, an open-source, self-hosted, proof-of-work captcha running inside the edge. No image grids, no traffic lights, no data leaving the store. We also use it inside Magento, replacing &lt;a href="https://developers.google.com/recaptcha" rel="noopener noreferrer"&gt;reCAPTCHA&lt;/a&gt; on the storefront forms.&lt;/p&gt;

&lt;p&gt;Does it work? Of the 404,149 visitors who were shown the full challenge page in our week, 85.6% never solved it. Some of the ones who did were bots too, which is fine: they paid for the privilege, and the other layers were still watching.&lt;/p&gt;

&lt;h2&gt;
  
  
  So when do we actually show the challenge?
&lt;/h2&gt;

&lt;p&gt;Putting every request through proof of work is the Anubis approach, and it is a bad experience for a shop. Shoppers are impatient, phones are slow, and every half second on a product page is conversion you will never see. So we are picky about it:&lt;/p&gt;

&lt;p&gt;**First visit from a browser: **an invisible check in the background, on the real page. The shopper reads the product title; the browser does the maths.&lt;/p&gt;

&lt;p&gt;**A doubtful score: **the same invisible check, with the page still served.&lt;/p&gt;

&lt;p&gt;**A high score: **a short interstitial that solves itself. A script that cannot run JavaScript is stuck at the door.&lt;/p&gt;

&lt;p&gt;**A cold hit on an expensive page: **catalogue search, deeply filtered categories. These cost the database real work, so a visitor arriving there with no history on the store proves itself before the query runs. That one-hour swarm of 31,201 addresses? Almost all of it was aimed at site search, and it hit this gate.&lt;/p&gt;

&lt;p&gt;**A store under pressure: **the puzzle gets harder for everyone who has not already earned trust. The cost follows the abuse, not the address, so rotating addresses does not help.&lt;/p&gt;

&lt;p&gt;Verified search engines never see a challenge. A returning shopper with a valid pass does not see it again for a while. And software that talks to the store through its own machine channels, like payment providers and stock systems, is judged by different rules, because it cannot solve a browser puzzle and should not have to. We wrote about how that played out during a real zero-day in &lt;a href="https://dev.to/blog/we-survived-stylesmuggler-magento-zero-day"&gt;We Survived StyleSmuggler&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the bots actually go
&lt;/h2&gt;

&lt;p&gt;Of everything we stopped, 46% was aimed at site search, 21% at layered-navigation filters, 19% at product, category and content pages, and 9% was exploit probes. Checkout and login together: under half a percent.&lt;/p&gt;

&lt;p&gt;Read that again. The bots are not after your checkout (some does for skimming). They are after your catalogue, and specifically the pages that are most expensive for you to render. Anti-bot on a store is as much a performance feature as a security one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Behind the edge
&lt;/h2&gt;

&lt;p&gt;Anti-bot is the front line, not the whole defence. Behind it sit a web application firewall, a &lt;a href="https://varnish-cache.org/" rel="noopener noreferrer"&gt;Varnish&lt;/a&gt; cache and a hardened, watched host. Every decision the edge makes is logged centrally, off the store, which is how every number in this post exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I deliberately left out
&lt;/h2&gt;

&lt;p&gt;There is not a single threshold, weight, cookie lifetime or exception list in this series. That is not modesty. Every number I publish is a number someone can tune against, and a list of what we skip is a list of where to aim.&lt;/p&gt;

&lt;p&gt;I am also not going to pretend it is perfect. A patient human in a real browser on a real home connection is, correctly, indistinguishable from a customer. The goal was never to stop everyone. It was to make automated abuse slow, expensive and noisy, while a real shopper notices nothing at all.&lt;/p&gt;

&lt;p&gt;Many small questions instead of one big wall. Detect first, decide later. Make bots pay in CPU instead of making people pay in patience.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Numbers in this post come from the edge logs of selected production stores we operate, over a given period — about 8.5 million requests. Store names are left out on purpose.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The anti-bot series: *&lt;/em&gt;&lt;em&gt;Overview&lt;/em&gt;** · &lt;em&gt;&lt;a href="https://www.storeframe.io/blog/openresty-vs-nginx-caddy-traefik-magento" rel="noopener noreferrer"&gt;Part 1: OpenResty vs NGINX, Caddy and Traefik&lt;/a&gt;&lt;/em&gt; · &lt;em&gt;&lt;a href="https://www.storeframe.io/blog/spot-fake-chrome-ja4-http2-fingerprinting" rel="noopener noreferrer"&gt;Part 2: How to Spot a Fake Chrome&lt;/a&gt;&lt;/em&gt; · &lt;em&gt;&lt;a href="https://www.storeframe.io/blog/how-to-build-a-bot-score" rel="noopener noreferrer"&gt;Part 3: How to Build a Bot Score&lt;/a&gt;&lt;/em&gt; · &lt;em&gt;&lt;a href="https://www.storeframe.io/blog/altcha-proof-of-work-turnstile-alternative" rel="noopener noreferrer"&gt;Part 4: Self-Hosted Proof of Work with ALTCHA&lt;/a&gt;&lt;/em&gt;.*&lt;/p&gt;

&lt;p&gt;StoreFrame runs Magento on your own cloud with this edge in front of every store: an OpenResty anti-bot layer, self-hosted proof-of-work challenges, a firewall, hardened containers and central logs. It is not an add-on; it is the platform.&lt;br&gt;
&lt;a href="https://www.storeframe.io" rel="noopener noreferrer"&gt;See how StoreFrame protects Magento stores&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>magento</category>
      <category>openresty</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>Agentic Operations, Not Agentic Commerce. What Do You Mean?</title>
      <dc:creator>Laurentius Judhianto</dc:creator>
      <pubDate>Mon, 21 Sep 2026 20:07:24 +0000</pubDate>
      <link>https://dev.to/laurnts/agentic-operations-not-agentic-commerce-what-do-you-mean-2m06</link>
      <guid>https://dev.to/laurnts/agentic-operations-not-agentic-commerce-what-do-you-mean-2m06</guid>
      <description>&lt;p&gt;Everyone talks about agentic commerce — agents that shop and check out for you. Fine idea. Not my idea. I’m focusing the half nobody looking at: the agent that helps the operators, not the one that buys.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So what’s your goal, Laurentius?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I get asked a version of this a lot: what are you actually doing here? There’s nothing genuinely new — nothing other developers couldn’t replicate. Where are you going with it? Fair question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here’s the brutally honest answer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My goal with StoreFrame was never to start an agency or another hosting company. I’m lazy, and I get bored repeating the same work over and over. Magento eats too much time, and I wanted to automate that time away — especially now that AI makes it possible.&lt;/p&gt;

&lt;p&gt;So the point is to &lt;strong&gt;automate everything&lt;/strong&gt; I already know how to do, so I’m free to go learn the thing I don’t. That’s it. That’s the whole motive.&lt;/p&gt;

&lt;h2&gt;
  
  
  StoreFrame begins as a library, not a hosting plan
&lt;/h2&gt;

&lt;p&gt;Here’s how I actually think about what I’ve built. I’m a bit like an AI with a small context window — learning languages is hard for me, even down to remembering syntax. So instead of keeping a thousand-page Notion of best practices I’ll never re-read, I poured all of it into a system. Every tuning lesson, every deploy recipe, every guardrail, every “don’t do the thing that broke production last year.” StoreFrame is that library of operational knowledge, turned into something that runs.&lt;/p&gt;

&lt;p&gt;That library is what scales me. It’s also the showcase — proof of what I can do, running live, instead of a slide deck claiming it. And the part that makes it agentic is that an AI can now read that library and act on it, on a real store, with the guardrails baked in. The knowledge isn’t advice anymore. It’s an operator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why operations, not commerce
&lt;/h2&gt;

&lt;p&gt;Agentic commerce points the agent at the shopper — go find this, compare those, fill the cart, check out. Agentic operations points it the other way entirely, at the shopkeeper. At the unglamorous, endless, invisible work of keeping a Magento store fast, secure, deployed, monitored, patched, and changed without anything breaking. The work nobody sees and everybody needs.&lt;/p&gt;

&lt;p&gt;And here’s why I think that’s the more interesting half. That operational work has always required a specialist — a developer, an agency, someone who knows where the bodies are buried in a Magento install. Specialists don’t scale; there are only so many hours in their day and only so many of them to hire. So merchants get stuck on a bad trade: either they under-buy attention and the store quietly rots, or they over-buy it and pay agency rates for changes that take ten seconds and a week of back-and-forth.&lt;/p&gt;

&lt;p&gt;Agentic operations is my answer to that trade. If the specialist’s knowledge lives in a system instead of in a head, the merchant stops buying hours and starts buying the knowledge itself — available at three in the morning, on every store at once, at the same standard every time. The specialist doesn’t disappear. They stop being the bottleneck.&lt;/p&gt;

&lt;h2&gt;
  
  
  Laying down the foundation for AI
&lt;/h2&gt;

&lt;p&gt;AI is going to write the code, Magento included — that part is inevitable. What it still needs is context. An AI is only as good as the information you can put in front of it, and the best way to collect, shape and serve that information is to own the layer everything passes through. The infrastructure.&lt;/p&gt;

&lt;p&gt;That’s why StoreFrame isn’t focused on Magento itself, but on everything around it, starting from the fundamentals. When you control the infrastructure, you control the flow of information: what comes in, what goes out, how it’s processed, where it came from and what happens to it next. That gives an AI a large and honest body of data to answer almost anything you ask — from product information down to raw web traffic.&lt;/p&gt;

&lt;p&gt;Owning that layer also buys room traditional hosting doesn’t leave you: custom MCPs, custom containers, custom data processing. Performance is tunable from every angle too — anti-bot handling, NGINX configuration, PHP profiling. I leave no stone unturned, because every stone is one more thing the AI gets to see.&lt;/p&gt;

&lt;h2&gt;
  
  
  Business is a system
&lt;/h2&gt;

&lt;p&gt;That’s the bet, stated plainly. Not an agent that replaces the customer’s search bar — an agent that works for the operator, and knows where every piece of data came from. Give it enough context and the guessing drops away; what’s left is a system that gets measurably better each time it runs.&lt;/p&gt;

&lt;p&gt;Because that is what a business is, isn’t it? A system, optimised over and over. I’m just handing the optimising to an AI agent that never gets bored.&lt;/p&gt;

&lt;p&gt;StoreFrame is Magento infrastructure built for AI. It runs every layer of the store — traffic, containers, deploys, logs — so an AI agent can see exactly what is happening and take over the routine work operators do by hand. On your own cloud.&lt;br&gt;
&lt;a href="https://www.storeframe.io" rel="noopener noreferrer"&gt;See how StoreFrame works&lt;/a&gt;&lt;/p&gt;

</description>
      <category>agentic</category>
      <category>vision</category>
      <category>automation</category>
    </item>
    <item>
      <title>How I Made a Luma Theme Faster Than Hyva on Hypernode — Without Spending Days</title>
      <dc:creator>Laurentius Judhianto</dc:creator>
      <pubDate>Wed, 10 Jun 2026 14:33:47 +0000</pubDate>
      <link>https://dev.to/laurnts/how-i-made-a-luma-theme-faster-than-hyva-on-hypernode-without-spending-days-2b5l</link>
      <guid>https://dev.to/laurnts/how-i-made-a-luma-theme-faster-than-hyva-on-hypernode-without-spending-days-2b5l</guid>
      <description>&lt;h1&gt;
  
  
  How I Made a Luma Theme Faster Than Hyva on Hypernode — Without Spending Days
&lt;/h1&gt;

&lt;p&gt;Hyva on Hypernode is the premium combo for a fast Magento store. But what if your existing Luma theme could beat it — without a rewrite or days of work?&lt;/p&gt;

&lt;p&gt;On paper it shouldn’t have been close. In one corner, Hyva on Hypernode — the premium combo everyone reaches for when a Magento store has to be fast. In the other, a five-year-old Luma theme with a dated design and images not even saved as WebP, running on a plain &lt;a href="https://hetzner.cloud/?ref=AnFggx6ANhCm" rel="noopener noreferrer"&gt;Hetzner&lt;/a&gt; box at roughly half the monthly cost. The old store answered faster.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A disclaimer before anything: this isn’t a knock on Hyva or Hypernode or the agency — all are genuinely excellent, and I’ll show you exactly where they win. I also don’t have access to the Hyva-on-Hypernode box, so I can’t run hardcore server-side metrics on it. This is Lighthouse plus an honest, perceived-speed comparison on two real, live stores — read it as “here’s what I saw,” not a lab verdict.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Credit where it’s due. &lt;a href="https://www.hyva.io/" rel="noopener noreferrer"&gt;Hyva&lt;/a&gt; is the most solid choice in open-source Magento 2 if you want real performance and a nice templating frontend — and the clever part is what it leaves out.&lt;/p&gt;

&lt;p&gt;It pairs Tailwind with Alpine.js (I first met Alpine over in the Laravel world) and sheds the pile of old library bloat Magento carried for years. Tailwind especially: you build a frontend without the CSS quietly ballooning.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Small personal aside. I’ve believed in this approach for a long time. Around 10 years ago, while I was still an employee, my habit of leaning on utility classes right in the markup earned me some side-eye — this was the Bootstrap-4-hung-the-moon era, before Tailwind existed.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Now utility-first is the industry standard, all AI generated is all Tailwind. Sigh.... I guess I was just early.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hyva is also a relief after the PWA detour. Magento 2’s frontend is hard enough on its own; PWA Studio made it dramatically harder — a completely separate stack with its own build and deploy procedure, foreign to anyone (inc. me) whose muscle memory is PHP. A lot of teams bounced off it. Hyva went the other way: modern interactivity without abandoning the .phtml and XML we already knew, and that’s a big part of why it won.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Split frontend and backend might still have its comeback — as AI-agent aces Javascript like Magnus Carlsen plays blindfolded Chess against 8 Grand masters, and something like Claude can design and build a frontend end to end in seconds, a decoupled stack could finally be worth the overhead. But that’s a story for another day.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Anyway — enough about Hyva. We all know it’s &lt;del&gt;good&lt;/del&gt; great. So why wouldn’t a merchant just go straight for it? Cost, mostly — and not mine, theirs. A Hyva license (recently made free, hooray) plus a premium modules from Amasty and host like Hypernode is a real monthly bill, and plenty of merchants simply don’t want to take it on.&lt;/p&gt;

&lt;p&gt;And it isn’t only hosting and licensing: moving to Hyva means every module you depend on has to be adapted to its frontend — Hyva-compatible builds or porting work — more cost and more time before you’ve sold a single extra product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting the ground
&lt;/h2&gt;

&lt;p&gt;So I set up an honest comparison, and laid out the rules first.&lt;/p&gt;

&lt;p&gt;The deal: a comparable Hypernode plan runs €637/month. The StoreFrame side is €340 — €250 for the management hub plus €90 for the Hetzner bare metal. Here's the hardware, side by side:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;StoreFrame (Luma Based)&lt;/th&gt;
&lt;th&gt;Hypernode (Hyva)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hetzner EX101 — bare metal&lt;/td&gt;
&lt;td&gt;Jackal S — dedicated tier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CPU&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Intel i9-13900 · 24 cores / 32 threads · up to 5.6 GHz&lt;/td&gt;
&lt;td&gt;AMD Ryzen · 12 cores&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;64 GB DDR5 ECC · 4400 MT/s&lt;/td&gt;
&lt;td&gt;64 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2× 1.92 TB Gen4 NVMe&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost / month&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;€340/mo (€250 hub + €90 server)&lt;/td&gt;
&lt;td&gt;€637/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The StoreFrame stack: OpenResty with Brotli, HTTP/3 and QUIC, three Redis instances, MariaDB, PHP-FPM, OpenSearch, our licensed Luma-based theme (which include &lt;a href="https://swissuplabs.com/page-speed.html" rel="noopener noreferrer"&gt;Swissup module for page speed&lt;/a&gt;), and Varnish, obviously. There’s no magic sauce — just properly tuned Ubuntu and Docker containers working in sync.&lt;/p&gt;

&lt;p&gt;And the honest handicaps, all on my side: the Luma store runs an older Magento 2.4.8 on PHP 8.4 — a basic theme - plain StoreFrame theme design I've made 5 years ago (back then the focus was not the design, just to get a proper single Magento2 webshop merge from multiple Magento1), so the design itself is dated. The images aren’t even optimized to WebP. The Hyva-on-Hypernode store is newer across the board, fresh - clean and sharp.&lt;/p&gt;

&lt;h2&gt;
  
  
  A level playing field
&lt;/h2&gt;

&lt;p&gt;One rule mattered more than the rest: keep it a level playing field. I deliberately left the exotic performance options on the shelf — no FrankenPHP, no ARM, none of the tricks StoreFrame can pull.&lt;/p&gt;

&lt;p&gt;Just a familiar, almost boring stack on plain x86 that any Magento team would recognise. The point wasn’t to win with something you’ve never heard of; it was to show how a setup everyone can relate to actually holds up. If it works on the boring stack, it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually saw
&lt;/h2&gt;

&lt;p&gt;You can scan them yourself:&lt;br&gt;
&lt;a href="https://fastartechniek.storeframe.store" rel="noopener noreferrer"&gt;fastartechniek.storeframe.store&lt;/a&gt; (Hetzner bare metal + StoreFrame Luma)&lt;br&gt;
&lt;a href="https://fastarshop.nl" rel="noopener noreferrer"&gt;fastarshop.nl&lt;/a&gt; (Hyva + Hypernode)&lt;/p&gt;

&lt;p&gt;At a glance, the Luma store doesn’t feel slow at all — it’s genuinely comparable. Try Add to Cart and filter on layered navigation, feels a snap on both.&lt;/p&gt;

&lt;p&gt;On uncached pages, and especially in the backend, it’s actually a tad quicker.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The fastartechniek.storeframe.store demo stays online only through the end of June 2026.&lt;/em&gt;&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%2Favxdmhtz6mosdjawo3if.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%2Favxdmhtz6mosdjawo3if.png" alt="StoreFrame Base Theme" width="800" height="451"&gt;&lt;/a&gt;&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%2F3m8v7hrnhxrcs1p68ktx.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%2F3m8v7hrnhxrcs1p68ktx.png" alt="Hyva Magento Theme" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which sounds strange until you remember what’s underneath: raw bare-metal performance versus a managed hosting platform.&lt;/p&gt;

&lt;p&gt;So the split is honest — we win on raw performance; Hypernode wins on stability, and Hyva wins on Lighthouse.&lt;/p&gt;

&lt;p&gt;Here are the hard figures — three identical pages on each store (the homepage, a category, and the exact same product), five runs each:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server response time (TTFB) — the bare-metal win&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;StoreFrame (Luma Based)&lt;/th&gt;
&lt;th&gt;Hyva&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Homepage&lt;/td&gt;
&lt;td&gt;16ms&lt;/td&gt;
&lt;td&gt;38ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Category page&lt;/td&gt;
&lt;td&gt;21ms&lt;/td&gt;
&lt;td&gt;106ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product page&lt;/td&gt;
&lt;td&gt;18ms&lt;/td&gt;
&lt;td&gt;49ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Google Lighthouse — mobile&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;StoreFrame (Luma Based)&lt;/th&gt;
&lt;th&gt;Hyva&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Homepage&lt;/td&gt;
&lt;td&gt;55&lt;/td&gt;
&lt;td&gt;66&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Category page&lt;/td&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product page&lt;/td&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;td&gt;90&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Google Lighthouse — desktop&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;StoreFrame (Luma Based)&lt;/th&gt;
&lt;th&gt;Hyva&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Homepage&lt;/td&gt;
&lt;td&gt;67&lt;/td&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Category page&lt;/td&gt;
&lt;td&gt;77&lt;/td&gt;
&lt;td&gt;48&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product page&lt;/td&gt;
&lt;td&gt;74&lt;/td&gt;
&lt;td&gt;99&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Measured early on a Sunday morning, around 5-6 AM CET — on purpose: that's when this B2B shop sees almost no real traffic, so neither store is fighting load. Google PageSpeed Insights (Lighthouse 13.3.0), median of 5 runs, across three identical pages — the homepage, a category, and the exact same product on both stores.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Server response” is PageSpeed Insight's server-response-time. The Luma side runs lean and stock — no WebP, no CDN, no critical-CSS, no JS deferral — to keep it a level playing field; the Hypernode store is proxied through Cloudflare, so this is origin to origin. Lighthouse scores still swing ±10–15 run-to-run on these heavy pages.&lt;/em&gt;&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%2Fkcdocpxy67kgsbchepmh.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%2Fkcdocpxy67kgsbchepmh.png" alt="Fastarshop Legacy Luma" width="800" height="665"&gt;&lt;/a&gt;&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%2Fm6c326mphtfonbd9gyim.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%2Fm6c326mphtfonbd9gyim.png" alt="Fastarshop New Hyva" width="800" height="666"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Legacy site — the tuned Luma theme on StoreFrame bare metal (fastartechniek.storeframe.store). The table below is its 10 runs:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://fastartechniek.storeframe.store/"&lt;/span&gt;   &lt;span class="c"&gt;# legacy: Luma theme, StoreFrame on bare metal&lt;/span&gt;
&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%-4s %-13s %-11s %-13s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; run &lt;span class="s2"&gt;"appconn(ms)"&lt;/span&gt; &lt;span class="s2"&gt;"ttfb(ms)"&lt;/span&gt; &lt;span class="s2"&gt;"srv-wait(ms)"&lt;/span&gt;
&lt;span class="nb"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;=()&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;1 &lt;span class="nv"&gt;$N&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;app ttfb &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{time_appconnect} %{time_starttransfer}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;sw&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s2"&gt;"BEGIN{printf &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;%.1f&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, (&lt;/span&gt;&lt;span class="nv"&gt;$ttfb&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="s2"&gt;)*1000}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%-4s %-13s %-11s %-13s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s2"&gt;"BEGIN{printf &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;%.1f&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="s2"&gt;*1000}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s2"&gt;"BEGIN{printf &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;%.1f&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="nv"&gt;$ttfb&lt;/span&gt;&lt;span class="s2"&gt;*1000}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$sw&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;wait&lt;/span&gt;+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$sw&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nv"&gt;median&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{v[NR]=$1} END{print (NR%2)?v[int(NR/2)+1]:(v[NR/2]+v[NR/2+1])/2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"----------------------------------------------"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"median server-wait over &lt;/span&gt;&lt;span class="nv"&gt;$N&lt;/span&gt;&lt;span class="s2"&gt; runs: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;median&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ms"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;run  appconn&lt;span class="o"&gt;(&lt;/span&gt;ms&lt;span class="o"&gt;)&lt;/span&gt;   ttfb&lt;span class="o"&gt;(&lt;/span&gt;ms&lt;span class="o"&gt;)&lt;/span&gt;    srv-wait&lt;span class="o"&gt;(&lt;/span&gt;ms&lt;span class="o"&gt;)&lt;/span&gt;
1    73.5          88.0        14.5
2    107.1         112.9       5.8
3    88.9          100.5       11.6
4    83.9          90.2        6.3
5    54.6          60.8        6.1
6    88.8          100.2       11.4
7    64.0          74.5        10.5
8    70.5          82.8        12.3
9    64.8          75.7        11.0
10   58.3          68.5        10.2
&lt;span class="nt"&gt;----------------------------------------------&lt;/span&gt;
median server-wait over 10 runs: 10.75 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Current production — the live Hyva store on Hypernode, behind Cloudflare (&lt;a href="http://www.fastarshop.nl" rel="noopener noreferrer"&gt;www.fastarshop.nl&lt;/a&gt;). The table below is its 10 runs:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://www.fastarshop.nl/"&lt;/span&gt;   &lt;span class="c"&gt;# current production: Hyva on Hypernode (Cloudflare)&lt;/span&gt;
&lt;span class="nv"&gt;N&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%-4s %-13s %-11s %-13s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; run &lt;span class="s2"&gt;"appconn(ms)"&lt;/span&gt; &lt;span class="s2"&gt;"ttfb(ms)"&lt;/span&gt; &lt;span class="s2"&gt;"srv-wait(ms)"&lt;/span&gt;
&lt;span class="nb"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;=()&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;1 &lt;span class="nv"&gt;$N&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;app ttfb &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s1"&gt;'%{time_appconnect} %{time_starttransfer}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;sw&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s2"&gt;"BEGIN{printf &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;%.1f&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, (&lt;/span&gt;&lt;span class="nv"&gt;$ttfb&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="s2"&gt;)*1000}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"%-4s %-13s %-11s %-13s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s2"&gt;"BEGIN{printf &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;%.1f&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="s2"&gt;*1000}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s2"&gt;"BEGIN{printf &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;%.1f&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="nv"&gt;$ttfb&lt;/span&gt;&lt;span class="s2"&gt;*1000}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$sw&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;wait&lt;/span&gt;+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$sw&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nv"&gt;median&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{v[NR]=$1} END{print (NR%2)?v[int(NR/2)+1]:(v[NR/2]+v[NR/2+1])/2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"----------------------------------------------"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"median server-wait over &lt;/span&gt;&lt;span class="nv"&gt;$N&lt;/span&gt;&lt;span class="s2"&gt; runs: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;median&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ms"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;run  appconn&lt;span class="o"&gt;(&lt;/span&gt;ms&lt;span class="o"&gt;)&lt;/span&gt;   ttfb&lt;span class="o"&gt;(&lt;/span&gt;ms&lt;span class="o"&gt;)&lt;/span&gt;    srv-wait&lt;span class="o"&gt;(&lt;/span&gt;ms&lt;span class="o"&gt;)&lt;/span&gt;
1    101.2         217.0       115.8
2    60.6          183.3       122.7
3    55.6          205.4       149.7
4    54.2          198.5       144.3
5    48.9          166.8       117.9
6    50.7          165.5       114.9
7    57.5          215.9       158.3
8    52.8          178.4       125.6
9    51.7          135.1       83.4
10   67.4          175.9       108.5
&lt;span class="nt"&gt;----------------------------------------------&lt;/span&gt;
median server-wait over 10 runs: 120.3 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;A note on method: both snippets run from a single fixed host, so these are origin-side server-response times — not Google’s PageSpeed vantage. The legacy store is hit directly; the production store is Cloudflare-fronted (served dynamically, proxied to the Hypernode origin), so its figure includes that extra network hop and isn’t pure origin think-time. What’s comparable here is the run-to-run median and the size of the gap, not the raw milliseconds.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So who wins?
&lt;/h2&gt;

&lt;p&gt;The honest answer splits along what you choose to measure. By TTFB — the time the server takes to answer at all — yes, we win, and not by a hair: uncached pages come back genuinely quick, and the cached ones land right alongside Hyva, near enough to call even.&lt;/p&gt;

&lt;p&gt;All of that with nothing fancy under the hood — just a plain, properly tuned stack. But if the number you live by is the Lighthouse score, Hyva still takes it here. Both are true at once; pick the benchmark that matches what your store actually needs.&lt;/p&gt;

&lt;p&gt;So did I actually win? On the one metric I set out to beat — raw server response — yes. Under the conditions I laid out (idle, cached, same page), a tuned Luma store on Hetzner bare metal answers faster than Hyva on Hypernode. And to be clear about the matchup: this is a Jackal S box (also Hypernode dedicated).&lt;/p&gt;

&lt;p&gt;Most Hypernode shops I've seen run Falcon — their common tier — and Hypernode scales well past this into far heavier, far pricier configurations that "might pull ahead again", yet I doubt as OpenStack is another layer of virtualization. I'm not racing those. The point was never “I beat a great platform” — it's that fast doesn't have to be expensive.&lt;/p&gt;

&lt;p&gt;If you have the budget, Hyva on Hypernode — and I mean that. It wins on Lighthouse, on the things Google rewards, on look-and-feel and the overall experience, and it’s built on newer technology that earns its price. (And if you genuinely want to go nuts on speed, Hyva on bare metal would be the ridiculous best of both — a post for another day.)&lt;/p&gt;

&lt;p&gt;Two honest caveats. On SEO: yes, Hyva's stronger Lighthouse scores feed Google's ranking signals — though how much weight Core Web Vitals really carry today is anyone's guess; nobody seems sure anymore.&lt;/p&gt;

&lt;p&gt;And on what I didn't measure: this was page-load speed, not load capacity — requests per second, behaviour under concurrency, tail latency at peak weren't tested here.&lt;/p&gt;

&lt;p&gt;That said, the StoreFrame box runs more CPU cores (24 vs 12) at the same 64 GB RAM on dedicated bare metal — so on paper it has more headroom and should hold up at least as well under pressure. Theoretically. That's a benchmark for another day.&lt;/p&gt;

&lt;p&gt;And you don’t even need bare metal to get here. Bare metal is the extreme — Fastarshop is a high-traffic store (more than 5 multi-sites), so it earns it. For most merchants, without that kind of traffic, a normal Hetzner cloud box with 64 GB of RAM is plenty: not quite as fast as raw bare metal, but comfortably fast enough. Same tuned stack, smaller bill.&lt;/p&gt;

&lt;p&gt;And that’s only the comparison against the premium end. Plenty of Magento 2 stores still run on shared hosting — cPanel and the like — oversubscribed and creaking, sharing a box with who-knows-what, especially with the daily dose of ZeroDay we face today. Against that, a properly tuned cloud server isn’t a close call; it’s night and day. If that’s where you are, the jump is bigger than anything in this post.&lt;/p&gt;

&lt;p&gt;So who is &lt;a href="https://dev.to/blog/hypernode-great-hosting-why-i-built-storeframe"&gt;StoreFrame&lt;/a&gt; for? Merchants who’d rather keep their Luma store — whether that’s about cost or any other reason — but still want a faster, modern-performing alternative. Let the server do the work. If that’s you, that’s exactly where we can help.&lt;/p&gt;

&lt;p&gt;One last thing. How does a plain server — root access, port 22, and nothing else — turn into any of this? That took me five years, and honestly it’s a story I haven’t seen told anywhere. I’ll give it &lt;a href="https://dev.to/blog/magento-2-performance-optimization-guide-2026"&gt;its own post&lt;/a&gt;. Trust me on that one — stay tuned.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rather keep your store and just make it faster? StoreFrame runs your existing Magento fully tuned on your own cloud — no re-platforming, no theme swap, operated by AI plus a human.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.storeframe.io" rel="noopener noreferrer"&gt;See how StoreFrame works&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>performance</category>
      <category>devops</category>
      <category>webdev</category>
      <category>saas</category>
    </item>
  </channel>
</rss>
