Checkout is the most critical page in any Magento 2 store. A one-second delay at this stage can cost you significantly more than the same delay on a product page — studies consistently show that conversion rates drop 7% for every second of checkout load time. Yet the Magento 2 checkout is notoriously heavy: it loads dozens of JavaScript components, makes multiple AJAX calls, and queries several database tables on every step.
This guide walks through the most impactful optimizations you can make to speed up your checkout — from quick wins to deep infrastructure changes.
Why Is Magento 2 Checkout So Slow?
Before optimizing, it helps to understand the bottlenecks:
- KnockoutJS + RequireJS overhead — The checkout is built on KnockoutJS with dozens of UI components, each requiring its own module load via RequireJS.
- Multiple AJAX requests on page load — Shipping methods, totals, customer data, and cart summaries are all fetched asynchronously.
- Full Page Cache exclusion — The checkout is not cacheable by FPC, so every request hits the application stack directly.
- Third-party payment/shipping extensions — These often add their own JS, CSS, and AJAX calls, compounding the overhead.
- Quote recalculation — Every time a customer changes address, coupon, or shipping method, Magento recalculates the entire quote.
1. Enable and Tune Full Page Cache (Even for Checkout)
While the checkout page itself cannot be fully cached, there are adjacent pages that can dramatically affect perceived checkout speed:
- The cart page should load instantly. Ensure it is FPC-cached.
- The minicart data can be warmed and stored in browser
localStorageto prevent repeated AJAX calls. - Use Varnish + ESI (Edge Side Includes) to cache static parts of checkout-adjacent pages.
More importantly, ensure FPC is enabled and warm for the rest of your store, because a fast product → cart → checkout funnel depends on each step being optimized.
# Verify FPC is enabled
bin/magento cache:status | grep full_page
# Flush and warm FPC
bin/magento cache:flush full_page
bin/magento cache:warm:fpc
2. JavaScript Optimization for Checkout
The checkout page is JavaScript-heavy by nature. The goal is to reduce parse time and eliminate blocking resources.
Merge and Bundle JS
Enable JS merging in Admin > Stores > Configuration > Advanced > Developer > JavaScript Settings:
- Merge JavaScript Files: Yes
- Enable JavaScript Bundling: Yes
- Minify JavaScript Files: Yes
For production, use the built-in bundling or switch to a more advanced bundler:
bin/magento setup:static-content:deploy -f --jobs 4
Use Advanced JS Bundling
Magento's default bundling is often suboptimal. Consider using the Magento Advanced Bundling approach with Grunt, which creates page-type-specific bundles so the checkout only loads what it needs.
Alternatively, community tools like Mirasvit JS Bundle or Yireo JS Bundler provide more sophisticated dependency graph analysis and can reduce initial checkout JS payload by 40–60%.
Defer Non-Critical Scripts
Move non-critical third-party scripts (analytics, chat widgets, social pixels) to load after the checkout has initialized:
<!-- layout/checkout_index_index.xml -->
<page>
<body>
<block name="your.analytics" template="..." after="-">
<!-- Load after checkout init -->
</block>
</body>
</page>
3. Reduce AJAX Calls During Checkout
Every AJAX call in checkout adds latency. Here's how to minimize them:
Customer Data Sections
Magento's "sections" system (customer data, cart, etc.) makes AJAX calls to /customer/section/load/ on various triggers. Audit which sections are being reloaded unnecessarily:
# Check which sections are defined
grep -r "sections" vendor/magento/module-customer/etc/frontend/sections.xml
Remove or merge section dependencies from custom modules that trigger unnecessary invalidations:
<!-- Your module's etc/frontend/sections.xml -->
<config>
<action name="your/controller/action">
<section name="cart"/>
<!-- Only include sections you actually modify -->
</action>
</config>
Preload Shipping Rates
If your store has a predictable default shipping address (e.g., country-level), pre-populate the shipping estimator with defaults to avoid an extra round trip when the customer first opens checkout:
// Plugin on ShippingInformationManagement
public function afterSaveAddressInformation($subject, $result, $cartId, $addressInformation)
{
// Cache shipping rates per region/country for faster subsequent loads
}
GraphQL for Checkout (Hyva / PWA)
If you're running Hyva Checkout or a PWA frontend, the GraphQL-based checkout is significantly faster than the default KnockoutJS checkout because it eliminates the RequireJS overhead entirely. Hyva Checkout reports 60–80% faster Time to Interactive compared to Luma checkout.
4. Database Optimization for Quote Operations
The quote and quote_item tables are write-heavy and often the source of checkout slowness.
Index Maintenance
Ensure these tables are regularly optimized:
-- Check table fragmentation
SELECT table_name, data_free, data_length
FROM information_schema.tables
WHERE table_schema = 'your_db'
AND table_name IN ('quote', 'quote_item', 'quote_address', 'quote_address_rate');
-- Optimize fragmented tables
OPTIMIZE TABLE quote, quote_item, quote_address;
Clean Up Expired Quotes
Old quotes bloat these tables and slow down queries. Set up a cron to purge them:
bin/magento cron:run --group index
Configure quote lifetime in Admin > Stores > Configuration > Sales > Checkout > Shopping Cart > Quote Lifetime (days). A value of 30 days is usually sufficient.
Add Missing Indexes
Some Magento installations are missing useful composite indexes on quote-related tables. Add them via a data patch:
$this->moduleDataSetup->getConnection()->addIndex(
$this->moduleDataSetup->getTable('quote'),
$this->moduleDataSetup->getIdxName('quote', ['customer_id', 'is_active']),
['customer_id', 'is_active']
);
5. Payment Method Optimization
Payment methods are one of the biggest hidden performance killers in checkout.
Audit Third-Party Payment Extensions
Every payment provider adds its own JS SDK to the checkout. Audit what's loading:
# Check which payment methods are active
bin/magento config:show payment | grep active
Disable payment methods you don't use. Each inactive-but-installed method still loads its configuration in checkout.
Load Payment SDKs Lazily
Payment SDKs (Stripe, PayPal, Mollie, etc.) should be loaded only when the customer reaches the payment step, not on initial page load. Most modern payment extensions support this — check your provider's configuration for "lazy load" or "defer script loading" options.
For PayPal specifically:
<!-- Disable PayPal in-context checkout if not needed -->
<config>
<default>
<paypal>
<wpp_usuk>
<in_context>0</in_context>
</wpp_usuk>
</paypal>
</default>
</config>
6. Server-Side Configuration
PHP Session Storage
Checkout is session-heavy. Storing sessions in files (the default) creates I/O contention under load. Move sessions to Redis:
// app/etc/env.php
'session' => [
'save' => 'redis',
'redis' => [
'host' => '127.0.0.1',
'port' => '6379',
'password' => '',
'timeout' => '2.5',
'persistent_identifier' => '',
'database' => '2',
'compression_threshold' => '2048',
'compression_library' => 'gzip',
'log_level' => '1',
'max_concurrency' => '6',
'break_after_frontend' => '5',
'break_after_adminhtml' => '30',
'first_lifetime' => '600',
'bot_first_lifetime' => '60',
'bot_lifetime' => '7200',
'disable_locking' => '0',
'min_lifetime' => '60',
'max_lifetime' => '2592000'
]
]
Enable HTTP/2
HTTP/2 multiplexing significantly reduces the overhead of multiple concurrent AJAX requests during checkout. Ensure your Nginx/Apache configuration has HTTP/2 enabled:
listen 443 ssl http2;
7. Measure Before and After
Don't optimize blindly. Use these tools to benchmark:
- Chrome DevTools → Network tab — Record a checkout flow and analyze waterfall
- WebPageTest.org — Test from different locations with filmstrip view
-
New Relic / Blackfire — Profile server-side checkout AJAX endpoints (
/shipping-information,/payment-information) -
Magento Profiler — Enable with
bin/magento dev:profiler:enablein staging
Key metrics to track:
- Time to First Byte (TTFB) on checkout page load
- Time to Interactive (TTI) — when the customer can actually interact
- Total JS payload on checkout
- Number of AJAX calls during a full checkout flow
Quick Wins Checklist
Before diving into complex optimizations, run through these quick wins:
- [ ] JS/CSS merging and minification enabled
- [ ] Expired quotes cleaned up (quote table < 500k rows)
- [ ] Sessions stored in Redis, not files
- [ ] Unused payment methods disabled
- [ ] FPC enabled and warming regularly
- [ ] HTTP/2 enabled on your server
- [ ] PHP OPcache configured correctly (see our OPcache guide)
- [ ] Third-party checkout extensions audited for JS bloat
Conclusion
Checkout performance is not a one-time fix — it's an ongoing practice. As you add payment providers, shipping integrations, and promotional logic, each one can quietly degrade the experience for your customers.
Start with the measurement, identify your biggest bottleneck (usually JS payload or AJAX calls), and work systematically down the list. Even a 30% improvement in checkout speed can meaningfully impact your conversion rate and revenue.
For stores on Hyva or PWA frontends, the path is even clearer: the component-based architecture makes it far easier to lazy-load and defer non-critical checkout functionality. If you're still on Luma and dealing with severe checkout slowness, migrating to Hyva Checkout is worth serious consideration.
Top comments (0)