Executive Summary
A slow Magento 2 checkout costs you conversions. This guide walks through the most common causes — from bloated quote tables and misconfigured payment methods to third-party script overhead — and the engineering fixes that actually move the needle.
Why Your Magento 2 Checkout Is Slow (And How to Fix It)
Checkout is where slow pages cost money directly. A two-second delay on a category page costs you engagement. A two-second delay between clicking “Proceed to Checkout” and the payment step appearing costs you the order. The customer has already committed enough to reach that point — the platform is the thing losing them.
Magento 2’s checkout is a single-page application backed by a series of API calls and a component tree that, on a store with accumulated extensions, can become genuinely unwieldy. The performance problems that appear there are almost never caused by a single issue. They are usually two or three things compounding — a bloated quote table, a payment method making a synchronous external call, and a third-party tag manager script sitting in the critical path — none of which is severe on its own, all of which add up to a checkout that feels broken.
This guide covers how to identify which of those problems you actually have, and what the engineering fix looks like for each.
Why checkout speed matters more than page speed elsewhere
Most Magento performance discussions focus on catalogue pages — product listings, search results, product detail pages. These matter for acquisition, for Core Web Vitals scores, for SEO. But the conversion impact of slow checkout is more direct and more measurable.
Customers who have reached checkout have already passed through your acquisition funnel. The cost of losing them there is the full customer acquisition cost plus the foregone order value. Customers who abandon during the payment step specifically — after entering their details but before confirmation — are the most valuable losses: they had the highest intent and the most friction tolerance.
Research across large ecommerce datasets consistently shows that for every additional second of checkout load time, conversion rate drops by 7–10% among users who experience it. On a checkout that takes 6 seconds to load instead of 2, that is not a 7% conversion drop — it is compounded across the full 4-second gap.
The other dimension is device and network. Your internal testing on a wired office connection will not surface checkout slowness that your mobile customers on 4G experience every day. The diagnostic work below should be done on a throttled mobile profile, not on your development machine.
Diagnosing the bottleneck before you fix anything
The most common mistake when addressing checkout performance is fixing the wrong thing. A checkout that feels slow on mobile may be slow for a different reason than a checkout that feels slow on desktop. Before touching any code, spend 30 minutes with the right diagnostic tools.
Chrome DevTools network waterfall
Open Chrome DevTools, navigate to the Network tab, and enable throttling to “Fast 3G” or the “Moto G Power” preset. Clear the cache, then walk through your checkout from a product page to the payment step. Look at the network waterfall for:
- Requests that block later requests. A single slow API call that nothing can load until it completes is a critical path dependency. These show as horizontal gaps in the waterfall where everything is waiting.
- Total request count on checkout initialisation. Magento’s checkout bootstrap involves fetching customer data, cart data, payment methods, and shipping methods. On stores with many payment providers or complex customer data, this can be 15–25 API calls. Each one adds to the time before the checkout UI is interactive.
- Large JavaScript bundles. The
checkout.jsbundle and extension-contributed JavaScript files. Look for files above 500KB uncompressed — they will appear as long horizontal bars during the parse phase.
Server Timing headers
If your server is configured to emit Server-Timing response headers — which Magento supports and which can be enabled in developer mode — these give you the breakdown of time spent server-side on each request: model loading, database queries, layout rendering, plugin execution. A checkout totals API call that takes 800ms server-side with 600ms attributed to a specific plugin is a much more precise diagnosis than “the checkout is slow.”
To check whether Server-Timing is available, look at the response headers for any Magento API call in your DevTools Network tab. If Server-Timing is present, the values there will tell you where the server is spending time. If it is not, and you have server access, enabling it in app/etc/env.php under 'x-magento-tags' configuration will surface it.
The specific API calls to watch
Magento 2 checkout makes a predictable set of API calls during initialisation and at each step. The ones that are most commonly slow:
| API call | What it does | Common slow cause |
|---|---|---|
GET /customer/section/load | Loads the customer data section that initialises the cart | Large customer quote with many items or abandoned quote data |
POST /shipping-information | Submits the shipping address and retrieves shipping methods and totals | Slow shipping carrier API calls, complex tax rules, ERP stock check |
GET /payment-information | Retrieves available payment methods and order totals | Synchronous payment gateway validation, many payment providers |
POST /payment-information | Places the order | Full order creation, ERP sync, all configured observers firing |
Identify which of these is taking the most time in your waterfall. The fix for a slow shipping-information call is different from the fix for a slow payment-information call, and attempting one when the problem is the other wastes time.
Cause 1: Bloated quote table and customer section loading
What it looks like
The GET /customer/section/load call takes more than 500ms. The checkout initialisation spinner runs for 2–4 seconds before any UI appears. The problem is often worse for returning customers than for guests.
What causes it
Magento stores the active quote — the cart — in the quote and quote_item tables. On stores that do not run regular quote cleanup, these tables accumulate abandoned quotes, partially processed quotes from failed orders, and quote records from customers who added items years ago. The section/load call loads the current customer’s cart data, but the query joins and index lookups slow down as the table grows.
A secondary cause is the customer data object that Magento computes when loading the customer section. On stores using custom customer attributes, B2B company account data, or extensions that add large data payloads to the customer section, this object can be significantly larger than it needs to be — and it is recomputed on every section/load call.
The fix
Quote table cleanup. Run the Magento cron job sales.clean.quotes if it is not already scheduled, or run it manually to clear quotes that have been inactive for more than the configured lifetime. Check Stores → Configuration → Sales → Checkout → Shopping Cart → Quote Lifetime — the default is 30 days, which is reasonable. The cleanup job is \Magento\Sales\Cron\CleanExpiredQuotes. After running it, check the row count on your quote table. Reductions from millions to hundreds of thousands of rows are common on stores that have not run this job.
Targeted table optimisation. After the cleanup, run OPTIMIZE TABLE quote; and OPTIMIZE TABLE quote_item; to reclaim space and rebuild the table statistics. On InnoDB tables that have had large deletions, this can significantly improve query performance.
Customer section data reduction. Review what is being added to the customer section via customer-data pooled sections. Each registered section adds to the payload and recompute cost. Extensions that add sections without checking whether their data is actually needed at checkout are a common source of bloat. Audit your sections.xml files across all active modules and remove sections that are not required for the customer-facing UI.
Measure quote table size before and after
Run SELECT COUNT(*) FROM quote; and SELECT COUNT(*) FROM quote_item; before and after the cleanup. On a store that has been running for 3+ years without scheduled cleanup, pre-cleanup counts above 2 million rows in quote_item are not unusual. Post-cleanup counts should be in the tens of thousands for a store with normal active cart volume. The query time reduction on section/load is directly proportional to the table size reduction.
Cause 2: Synchronous payment method validation and loading
What it looks like
The GET /payment-information call takes more than 1 second. In the waterfall, it is blocking the payment step from rendering. The problem is often inconsistent — fast sometimes, slow others — which indicates a network call to an external service rather than a purely local computation.
What causes it
Magento loads available payment methods by calling each configured payment provider’s isAvailable() method. If a payment provider’s isAvailable() check makes a synchronous HTTP call to an external API — to verify the account is active, to check feature flags, or to fetch configuration — that call sits in the critical path of your checkout loading time. Every millisecond of latency to that external service adds directly to your checkout load time.
The same issue occurs with payment providers that load their JavaScript SDK synchronously. If Klarna, Stripe, Braintree, or another provider’s script is loaded in a <script> tag without async or defer, it blocks the browser’s rendering pipeline until the external script has loaded and executed.
The fix
Audit your configured payment methods. In Magento admin, go to Stores → Configuration → Sales → Payment Methods. Every enabled payment method adds to the payment-information call. Disable any payment provider that is not actively being used — it is common to find test credentials for payment providers that were evaluated and never deployed, still enabled in production configuration.
Review isAvailable() implementations. For each active payment provider, review its isAvailable() method (in the module’s Model/Payment class or its Gateway/Validator equivalents). Methods that call curl_exec, use $this->_httpClient, or instantiate an HTTP client should be flagged. If the external call cannot be removed, the result should be cached — a payment method availability check does not need to be a fresh HTTP call on every checkout page load.
Move payment scripts to async loading. Locate where payment provider JavaScript is included — usually in a checkout_index_index.xml layout file or in a require-config.js in the payment module. Scripts that must be loaded can be deferred until after the checkout UI is interactive by restructuring their loading as a require() call triggered after the payment step renders, rather than as a synchronous include in the page head.
Cause 3: Third-party scripts blocking checkout render
What it looks like
In the DevTools waterfall, several external domain requests — Google Tag Manager, Meta Pixel, Hotjar, live chat scripts — appear in the critical path before the checkout UI renders. On mobile with throttled network, these add 1–3 seconds individually.
What causes it
Third-party tags that are injected into the <head> without async or defer block the browser’s HTML parser. On the checkout page specifically, any script that fires before the Magento checkout JavaScript has initialised can delay the component mounting that makes the checkout interactive. Tag Manager containers that load synchronously, or that load many tags simultaneously on checkout, are a consistent source of this problem.
The second issue is tag firing logic. A Tag Manager setup that fires every configured tag on every page — including checkout pages — is loading analytics, remarketing, and session recording scripts that may not need to fire at all on the checkout path, or that can fire after the page is interactive rather than during initialisation.
The fix
Audit what fires on checkout. In Google Tag Manager (or equivalent), review which tags are configured to fire on Page View or All Pages triggers. Tags that are not specifically required for checkout — top-of-funnel acquisition tags, content recommendation scripts, contextual advertising pixels — should either be excluded from checkout URLs or deferred until after window.load rather than DOM ready.
Move GTM loading to async. The standard GTM snippet loads the container script synchronously. Replacing the <script> include with an async version — loading it after the initial paint rather than in the <head> — removes it from the critical path. For checkouts where GTM is used for purchase event tracking, the tracking call can still fire correctly after the page is interactive; it does not need to load before the checkout renders.
Identify render-blocking resources. In DevTools, switch to the Performance panel and record a checkout load. Look for long tasks (shown in red in the main thread timeline) that occur before the checkout component mounts. Third-party scripts that execute synchronously during this window are candidates for deferral. console.log timing marks added temporarily around Magento’s customerData.reload() call will show you exactly when the customer section loading completes relative to the third-party script execution.
GTM changes carry their own risk
Modifying Tag Manager firing rules can affect conversion tracking, remarketing audience building, and analytics accuracy. Any changes to what fires on checkout — especially if you are deferring a purchase event tag or removing a tag from checkout pages — should be validated against your analytics data for at least a week before being treated as permanent. Involve whoever manages your digital marketing tracking before making these changes unilaterally.
Cause 4: Varnish and FPC misconfiguration at checkout
What it looks like
Checkout page loads are consistently slow even before any API calls are made — the initial HTML response itself is slow. In the DevTools waterfall, the first request to /checkout has a Waiting (TTFB) value above 800ms. Or: checkout pages are fast for the first visit but slow on return visits, or vice versa.
What causes it
Magento’s full-page cache correctly excludes the checkout page itself from FPC — the /checkout URL contains customer-specific data and cannot be cached at the page level. However, the assets that the checkout page loads — JavaScript bundles, CSS, static assets — can and should be served from cache. A Varnish misconfiguration that also excludes static assets from caching, or that treats checkout requests as a reason to purge related cached assets, adds latency to every checkout page load.
A more specific problem is session cookie handling. If Varnish is configured to bypass the cache for any request carrying a PHPSESSID cookie — which is a common default VCL configuration — then checkout page loads from returning visitors are bypassing Varnish entirely and hitting the origin for every request, including for assets that are identical for every user.
The fix
Check your VCL cookie handling. In your Varnish VCL’s vcl_recv function, review how PHPSESSID and private_content_version are handled. The correct Magento VCL strips these cookies from requests for static assets (/_nuxt/, /static/, pub/static/, pub/media/) so that Varnish can cache them, while passing the cookies through for dynamic Magento requests. If your VCL is passing cookies through for all requests, static assets are not being cached for logged-in users.
Review the Magento Varnish VCL against your installed version. Magento provides a generated VCL via bin/magento varnish:vcl:generate. If your production VCL was generated for an older Magento version and has not been regenerated since upgrades, it may be missing handling for newer Magento request patterns. Regenerate it, diff it against your current production VCL, and review any differences before deploying.
Verify Varnish is actually being hit. Check the X-Cache response header on checkout page requests in DevTools. A value of HIT means Varnish served the response. A value of MISS means the request went through to the origin — which for the checkout HTML itself is expected, but for static asset requests on the checkout page is not. Consistent MISS responses for CSS and JavaScript assets on checkout indicate a caching bypass problem.
Cause 5: Shipping and tax estimator API latency
What it looks like
The POST /shipping-information call takes more than 2 seconds. The checkout hangs after the customer submits their delivery address, before the shipping method options appear. The problem is consistent regardless of the customer’s device or network speed — the latency is server-side.
What causes it
When Magento receives the shipping address submission, it calls each configured shipping carrier’s rate calculation method. Live-rate carriers — DHL, FedEx, DPD, Royal Mail Click and Drop — make external API calls to retrieve rates in real time. If the carrier’s API is slow, or if the Magento implementation does not have appropriate timeouts configured, the checkout waits for every carrier to respond before showing any options.
Tax calculation via a third-party service (Avalara, Vertex) adds the same latency pattern — an external API call in the critical path of the totals calculation, called synchronously when the shipping address is submitted.
A secondary cause is complex cart price rules. Magento evaluates all active cart price rules when calculating totals. On stores with dozens of active rules — particularly rules with complex conditions involving custom attributes — this calculation can take several hundred milliseconds in PHP before the external API calls are even considered.
The fix
Set explicit timeouts on all carrier API calls. Review your shipping carrier configurations in Stores → Configuration → Sales → Delivery Methods. Every live-rate carrier has a configurable connection timeout and response timeout. These default to values like 30 seconds in some integrations, which means a carrier API outage turns into a 30-second checkout hang. Set connection timeout to 3 seconds and response timeout to 5 seconds for all live-rate carriers.
Disable live-rate carriers that are not being offered at checkout. If a carrier is configured but its rates are never actually shown to customers — because they are always undercut by a flat-rate option, or because the carrier is only used for internal cost calculation — disable it in the checkout configuration. It is still making its external API call on every shipping address submission regardless.
Cache shipping rates where possible. Some carrier modules support rate caching — storing the result of an API call for a given origin/destination/weight combination for a configurable period. For stores where shipping rates do not change intraday, a 30–60 minute rate cache removes the external API call from the critical checkout path entirely.
Profile cart price rule evaluation. Enable Magento’s built-in query logging temporarily (bin/magento dev:query-log:enable) and submit a shipping address in staging. Check the query log for the totals calculation phase. Rules that are evaluating large customer segment queries or joining against large custom attribute tables during the checkout totals calculation are candidates for simplification or restructuring.
Before and after: what to measure
Fixes without measurement produce anecdote, not evidence. Before making any changes to a production checkout, record baseline metrics for:
| Metric | How to measure | Target |
|---|---|---|
| Checkout TTFB | DevTools Network, Waiting column on /checkout request | < 300ms |
section/load response time | DevTools Network, filter by section | < 400ms |
shipping-information response time | DevTools Network, filter by shipping | < 1,500ms |
payment-information response time | DevTools Network, filter by payment | < 800ms |
| Checkout abandonment rate | Analytics platform, checkout funnel report | Baseline + track weekly |
| Checkout conversion rate | Orders / checkout initiations | Baseline + track weekly |
Record these on a throttled mobile profile (Fast 3G, mid-range device) as well as on desktop. The mobile numbers are typically 2–3x worse than desktop and represent the majority of your traffic on most UK ecommerce stores.
After each change, re-measure the specific API calls affected by that change before deploying the next fix. This prevents the common situation where three changes are deployed simultaneously and the improvement cannot be attributed to any specific one.
When the checkout performance problem is structural
The fixes above address the most common causes of checkout slowness in isolation. If you have worked through all of them and the checkout is still slow, the remaining causes are typically structural:
The checkout is running on underpowered infrastructure. PHP-FPM pool sizes, OPcache configuration, and database server resources that were appropriate for your traffic 18 months ago may no longer be. A checkout that is slow because the server is consistently running at 70–80% CPU during peak traffic is not a code problem — it is a capacity or configuration problem.
The checkout JavaScript bundle is too large to optimise incrementally. On a Luma-based store with 20+ extensions contributing JavaScript, the checkout bundle may be 2–3MB uncompressed. Individual optimisations reduce this at the margin. A Hyvä migration addresses it structurally by replacing the RequireJS-based bundle architecture with a component model that does not accumulate in the same way.
The underlying Magento build has accumulated technical debt that affects checkout specifically. Extension conflicts on the checkout path, database table sizes that resist cleanup due to foreign key constraints, or cron-based processes that compete with checkout processes for database connections are problems that require a structured audit rather than targeted fixes.
For checkout problems that persist after working through this guide, a Magento technical assessment will identify whether the root cause is something addressable with targeted engineering work or whether it requires a broader performance remediation engagement. The Magento technical audit post covers the broader set of risks that typically coexist with checkout performance problems on stores that have been in production for several years.
Need help with Magento?
Planning Magento for your small business or start-up?
We help brands choose the right platform, launch lean Magento builds, and scale without wasting budget.
Related services: Magento project rescue, Hyvä and performance optimisation, and marketplace operations.
Free 20-minute call · No hard sales, ever.
