BigCommerce stores running Stencil themes on Akamai’s CDN have a performance ceiling most agencies never touch. They’ll install a lazy-load app, toggle on AMP, and call it optimization. That’s not optimization. That’s checkbox work. Real Core Web Vitals improvements on BigCommerce require reading cache headers, auditing the CSSOM parse chain, deferring scripts the Script Manager won’t let you defer natively, and understanding exactly where Akamai’s auto-WebP pipeline breaks down.
This guide is the playbook we use on every BigCommerce development engagement where speed is the deliverable. Every recommendation references a specific admin path, a real response header, or a Stencil template file. If you’re looking for generic “optimize your images” advice, this isn’t it.
Akamai CDN Cache Headers: Reading X-BC-Cache-Status and X-Akamai-Transformed
Every response from a BigCommerce storefront passes through Akamai’s edge network before it reaches the browser. The cache headers in that response tell you exactly what happened at the edge. Most developers never look at them. That’s a mistake, because a single cache MISS on your homepage’s HTML document adds 200-400ms to Time to First Byte.
X-BC-Cache-Status: HIT vs MISS
Open Chrome DevTools. Click the Network tab. Reload your storefront homepage. Click the first document request. Scroll down to the Response Headers section. Look for X-BC-Cache-Status.
A value of HIT means Akamai served the page from its edge cache. The request never touched BigCommerce’s origin servers. TTFB on a HIT typically falls between 30ms and 120ms depending on the visitor’s proximity to the nearest Akamai PoP (Point of Presence).
A value of MISS means the request went all the way back to BigCommerce’s origin. TTFB on a MISS jumps to 400ms-1,200ms. You’ll see MISS responses in three situations: the page has never been cached, the cache TTL expired, or a query parameter on the URL forced a cache bypass.
The third scenario is the silent killer. Marketing UTM parameters like ?utm_source=facebook&utm_medium=cpc create unique cache keys. Every unique URL combination generates a separate MISS. A single Facebook campaign driving traffic with five different UTM combinations means five separate origin fetches for what is functionally the same page. Akamai’s default BigCommerce configuration does strip some query parameters, but not all. Check your landing page URLs with their full query strings in DevTools to confirm you’re actually getting HITs.
X-Akamai-Transformed
This header appears on image responses. It tells you Akamai modified the image before delivering it. A typical value looks like 9 120187 0 pmb=mRUM,3. The presence of this header confirms the image went through Akamai’s Image Manager pipeline. If you don’t see it on a JPEG or PNG response, the image bypassed optimization entirely.
You can verify auto-WebP conversion by comparing the Content-Type response header against the original file extension. Request a .jpg file. If your browser sent Accept: image/webp in the request headers and Akamai converted it, the response Content-Type will read image/webp even though the URL still ends in .jpg. This is transparent negotiation. It works automatically. But you should verify it’s actually happening, because certain image dimensions and file sizes fall below Akamai’s conversion threshold and get served as their original format.
These headers matter because they’re the fastest diagnostic tool for BigCommerce SEO performance issues. Before you touch a single template file, check your cache hit rate. If your most-trafficked pages are returning MISS, nothing else you do will move the needle on TTFB.
The Critical Render Path in BigCommerce Stencil Themes
Stencil themes compile SCSS to CSS during the stencil bundle step. The output lands in assets/css/ as a single concatenated stylesheet that the browser must download and parse before it can render any visible content. This file is render-blocking by definition. Every unused CSS rule inside it adds parse time to First Contentful Paint.
The assets/scss/ Compilation Pipeline
Stencil’s SCSS structure lives in assets/scss/. The main entry point is typically theme.scss, which imports partials from subdirectories: components/, layouts/, settings/, and utilities/. When you run stencil bundle, the Node-Sass compiler (or Dart Sass in newer CLI versions) resolves every @import and produces a single CSS file.
The problem is cumulative. Cornerstone ships with SCSS partials for every component: product cards, carousels, blog layouts, gift certificate forms, compare pages, and wishlist modals. A store that doesn’t use wishlists or product comparison still ships those CSS rules to every visitor. On a typical Cornerstone installation, 30-40% of the compiled CSS is dead code.
Measuring this is straightforward. Open Chrome DevTools, press Cmd+Shift+P (or Ctrl+Shift+P on Windows), type “Coverage,” and click “Show Coverage.” Reload the page. The CSS file will show a red/green bar. Red is unused bytes. On one BigCommerce product page we audited, 847KB of CSS loaded but only 312KB was used on that page. That’s 535KB of dead CSS the browser parsed for nothing.
Reducing CSSOM Parse Time
Remove the @import lines for components your store doesn’t use. This means opening assets/scss/theme.scss and commenting out or deleting imports for unused partials. After removing them, run stencil bundle and confirm the compiled CSS file shrinks.
For stores that use most components but only on specific pages, consider splitting the CSS. Stencil allows injecting page-specific styles through the {{inject}} Handlebars helper combined with <style> blocks in individual template files. Move component-specific CSS into inline <style> tags on the templates that need them. The main stylesheet stays lean. The tradeoff is maintenance complexity. You’re managing CSS in two places instead of one. For stores where LCP is the priority metric, it’s worth it.
Critical CSS extraction is another approach. Identify the above-fold styles for your homepage, category pages, and product pages. Inline those styles in a <style> tag in the <head> of templates/layout/base.html. Then add media="print" onload="this.media='all'" to the main stylesheet’s <link> tag to defer its load. This technique drops FCP by 300-600ms on CSS-heavy themes.
Script Manager Load Order and Total Blocking Time
BigCommerce’s Script Manager lives at Storefront > Script Manager in the admin panel. It lets you add JavaScript to your storefront in three locations: Header, Footer, or a specific page. It offers two placement options for each: “Essential” or “Analytics/Tracking.” Most merchants and agencies dump everything into Footer placement and assume it won’t affect performance.
That assumption is wrong.
Footer Scripts Still Block Total Blocking Time
A script placed in the footer loads after the HTML parser reaches the </body> tag. It doesn’t block initial render. But if that script executes synchronously and runs for 200ms+, it blocks the main thread. Every millisecond of main thread blocking during user interaction counts toward Total Blocking Time (TBT). TBT is the lab proxy for Interaction to Next Paint (INP), which became a Core Web Vital in March 2024.
Script Manager’s UI does not expose an async or defer attribute option. You can add a script, choose its location, and set it to load on all pages or specific pages. That’s it. The script tag it generates is synchronous by default.
The setTimeout Workaround
You can’t add async through the Script Manager interface. You can, however, wrap your script’s execution in a setTimeout with a zero-millisecond delay. This pushes execution to the next available idle frame.
<script>
setTimeout(function(){
// Your tracking pixel or chat widget initialization
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXX');
}, 0);
</script>
This doesn’t truly defer the script. It yields to the browser’s rendering pipeline first. The practical result: the main thread processes the current render frame, paints pixels, and then picks up the script execution. On a page with five tracking scripts, wrapping each in setTimeout(fn, 0) reduced TBT from 1,800ms to 640ms in our testing.
For scripts that don’t need to fire until user interaction (live chat, feedback widgets), use an Intersection Observer or scroll event listener instead. Load the script only when the user scrolls past the fold or clicks a trigger element. This eliminates their TBT contribution entirely.
BigCommerce Image CDN and Auto-WebP Conversion
BigCommerce serves product images, category images, and CMS content images through Akamai’s Image Manager. When a browser sends an Accept: image/webp header (Chrome, Firefox, Edge, and Safari 14+ all do), Akamai automatically converts JPEG and PNG source files to WebP format. The URL doesn’t change. The conversion happens at the edge.
Where Auto-WebP Works
Product images uploaded through Products > [Product Name] > Images & Videos in the BigCommerce admin are eligible for auto-WebP. Category images set via Products > Product Categories > [Category] > Category Image also convert. Images placed in CMS pages using the built-in WYSIWYG editor and hosted on BigCommerce’s CDN (URLs starting with cdn11.bigcommerce.com or your store’s CDN subdomain) convert as well.
WebP typically delivers 25-35% smaller file sizes compared to JPEG at equivalent visual quality. On a category page displaying 24 product thumbnails, that savings compounds. We measured 1.2MB of JPEG thumbnails reduced to 780KB of WebP on one catalog page. That’s 420KB less data over the wire, which directly impacts Largest Contentful Paint on mobile connections.
Where Auto-WebP Breaks Down
SVG files are never converted. Akamai’s Image Manager doesn’t process vector formats. This is expected behavior, not a bug. SVGs are already lightweight and resolution-independent.
PNG files with alpha channel transparency sometimes lose transparency during WebP conversion. We’ve seen this on product images with transparent backgrounds, specifically PNGs using 8-bit alpha channels rather than simple binary transparency. The converted WebP renders with a white or black background where the transparency should be. Checking for this requires visual inspection. Load the page in Chrome, right-click the image, select “Open image in new tab,” and confirm the background is transparent. If it’s not, the conversion broke it.
The fix is to add the image as a WebP file directly. Upload a manually converted WebP with lossless compression and alpha preservation instead of relying on Akamai’s automated pipeline. BigCommerce accepts WebP uploads for product images as of 2023.
Images hosted on external domains (Cloudinary, Imgix, or your own S3 bucket) don’t pass through Akamai’s pipeline at all. They serve in whatever format you uploaded them. If you’re using external image sources for product page content, you’re responsible for your own WebP conversion and responsive sizing.
Case Study: Mobile LCP From 4.1s to 1.9s on a 3,200-SKU Store
A home goods retailer running BigCommerce Enterprise with a heavily customized Cornerstone theme came to us with a problem. Their Google Search Console Core Web Vitals report showed 78% of mobile URLs failing LCP. PageSpeed Insights scored the homepage at 31 on mobile. Their competitor on Shopify was outranking them on branded product terms, and page speed was a factor.
The Baseline Audit
We ran a WebPageTest audit from Dulles, VA on a Moto G4 profile with a 4G connection. The results were clear.
- LCP: 4.1 seconds (the hero carousel’s third slide image)
- FCP: 2.3 seconds (blocked by 1.1MB of unminified CSS)
- TBT: 1,420ms (Google Tag Manager loading 14 tags synchronously)
- CLS: 0.18 (product grid images without width/height attributes)
The Three Changes That Moved LCP
Change 1: Replace the hero carousel with a static WebP image. The carousel loaded three 1920×800 JPEG images on page load, even though only the first slide was visible. We replaced it with a single static hero image, manually converted to WebP at quality 82, and served it with explicit width and height attributes. This eliminated two unnecessary image requests and cut the hero image payload from 2.1MB (three slides) to 187KB (one optimized WebP). LCP dropped from 4.1s to 2.8s from this change alone.
Change 2: Lazy-load below-fold product grid images. The homepage displayed a “Featured Products” grid below the hero. Twelve product images loaded eagerly on every page view. We added loading="lazy" to each <img> tag in the templates/components/products/card.html Stencil partial. We also added the decoding="async" attribute to prevent image decode from blocking the main thread. This freed up bandwidth and main thread time for above-fold content. LCP dropped further to 2.2s.
Change 3: Move Google Analytics to a Partytown worker thread. Google Analytics (GA4) and Google Tag Manager were executing on the main thread, adding 380ms of blocking time during page load. We implemented Partytown, a library that relocates third-party scripts to a web worker. The GA4 snippet, Facebook Pixel, and Hotjar scripts all moved off the main thread. TBT dropped from 1,420ms to 310ms. LCP dropped to 1.9s because the main thread was free to prioritize rendering.
The Results
After deploying these three changes, we re-tested with WebPageTest and monitored CrUX data over 28 days.
- LCP: 1.9s (from 4.1s). 54% improvement.
- FCP: 1.4s (from 2.3s). 39% improvement.
- TBT: 310ms (from 1,420ms). 78% improvement.
- CLS: 0.04 (from 0.18). Width/height attributes on all images.
- Mobile PageSpeed score: 81 (from 31).
Google Search Console showed mobile URLs passing Core Web Vitals jumped from 22% to 89% within the 28-day CrUX collection window. Organic mobile traffic increased 23% over the following 60 days. The speed improvement wasn’t the only factor. We made SEO changes simultaneously. But the Core Web Vitals pass rate was a prerequisite for the ranking gains.
Third-Party Script Audit Methodology
Third-party scripts are the single largest source of Total Blocking Time on BigCommerce stores. Every chat widget, analytics tag, remarketing pixel, and A/B testing tool adds JavaScript that executes on the main thread. The cumulative impact is often worse than every other performance issue combined.
Step 1: Inventory Every External Script
Open Chrome DevTools. Go to the Network tab. Filter by “JS.” Reload the page. Sort by domain. Every domain that isn’t your store’s domain or cdn11.bigcommerce.com is a third-party script. Document each one: its source domain, its file size (transferred), and its initiator chain (which script loaded it).
Common offenders on BigCommerce stores include Google Tag Manager (which itself loads additional scripts), Klaviyo’s tracking snippet, Yotpo reviews, JustUno popups, Lucky Orange session recording, and various Facebook/TikTok/Pinterest pixels.
Step 2: Measure Individual Impact
Chrome DevTools Performance tab gives you a flame chart. Record a page load. Look for long tasks (yellow blocks exceeding 50ms). Click each long task to see which script caused it. This tells you exactly which third-party script is responsible for each chunk of main thread blocking time.
You can also use the “Block request URL” feature in the Network tab. Right-click a script, select “Block request URL,” reload, and measure the performance difference. If blocking a script drops TBT by 400ms, you’ve found a high-impact target.
Step 3: Categorize and Prioritize
Sort scripts into three buckets. Essential scripts generate revenue or collect data you act on weekly. Useful scripts provide value but aren’t business-critical. Dead scripts were added by a previous agency, a terminated A/B test, or a tool you’re no longer paying for.
Remove dead scripts immediately. For useful scripts, apply the setTimeout deferral technique described in the Script Manager section. For essential scripts, consider Partytown or a similar web worker approach to move execution off the main thread.
Step 4: Set a Performance Budget
Define a maximum TBT budget for third-party scripts. We use 500ms as the ceiling. Every new script must be evaluated against this budget before installation. If adding a new chat widget pushes TBT from 450ms to 700ms, the widget needs to load conditionally (on interaction, not on page load) or it doesn’t get added.
Font Optimization: Subsetting, display:swap, and Preloading
Custom fonts on BigCommerce Stencil themes cause two performance issues. They add network requests that delay text rendering. They cause layout shifts when the browser swaps from a fallback font to the custom font after loading.
font-display: swap
Every @font-face declaration in your theme’s SCSS should include font-display: swap. This tells the browser to render text immediately using a system fallback font, then swap in the custom font once it finishes loading. Without it, the browser hides text entirely until the font downloads. On slow 3G connections, that invisible text period lasts 1-3 seconds.
In Stencil themes, @font-face rules typically live in assets/scss/settings/foundation/type/_settings.scss or a similar typography partial. Add font-display: swap; to each declaration. If your theme loads Google Fonts via the theme editor (under Storefront > My Themes > Customize > Styles > Typography), Stencil generates the @font-face rules dynamically. In that case, you’ll need to override the font loading in templates/layout/base.html by adding a <link> tag with the &display=swap parameter appended to the Google Fonts URL.
Font Subsetting
Most stores use one or two font weights and no special characters beyond standard Latin. A full Google Font file for a typeface like Montserrat includes Cyrillic, Vietnamese, and extended Latin glyphs. The regular weight alone is 98KB. Subset it to Latin-only characters, and it drops to 23KB.
Use a tool like pyftsubset (from the fonttools Python library) or the Glyphhanger CLI to strip unused character ranges. Generate WOFF2 files, which compress 30% better than WOFF. Host the subset font files in your theme’s assets/fonts/ directory and reference them with a local @font-face rule instead of loading from Google’s CDN. This eliminates the DNS lookup, TCP connection, and TLS handshake to fonts.googleapis.com and fonts.gstatic.com. Two fewer connection setups, each saving 100-300ms on mobile.
Preloading Critical Fonts
Add a <link rel="preload"> tag for your primary heading and body fonts in the <head> of templates/layout/base.html.
<link rel="preload" href="{{cdn 'assets/fonts/montserrat-regular-latin.woff2'}}" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="{{cdn 'assets/fonts/montserrat-bold-latin.woff2'}}" as="font" type="font/woff2" crossorigin>
The crossorigin attribute is required even for same-origin fonts. Omitting it causes the browser to fetch the font twice. The {{cdn}} Handlebars helper ensures the URL points to BigCommerce’s CDN, so the font benefits from Akamai’s edge caching.
Limit preloads to two fonts maximum. Each preload competes for bandwidth with other critical resources. Preloading four or five font files can actually delay LCP by saturating the connection during the critical loading phase.
Lazy Loading Implementation in Stencil
Native browser lazy loading with loading="lazy" is the simplest and most effective approach for BigCommerce Stencil themes. It requires no JavaScript library. It works in Chrome, Firefox, Edge, and Safari 15.4+.
Product Card Images
The product card template lives at templates/components/products/card.html in Cornerstone. Find the <img> tag that renders the product thumbnail. Add loading="lazy" and decoding="async" to it.
<img
class="card-image"
src="{{getImageSrcset image 1x=theme_settings.productgallery_size}}"
alt="{{image.alt}}"
title="{{image.alt}}"
width="{{theme_settings.productgallery_size}}"
height="{{theme_settings.productgallery_size}}"
loading="lazy"
decoding="async"
>
Do not add loading="lazy" to above-fold images. The hero image, logo, and any product images visible without scrolling should load eagerly. Lazy loading above-fold images delays LCP because the browser won’t start fetching them until they enter the viewport intersection zone (typically 1,250px from the viewport on Chrome).
Category Page Product Grids
Category pages in Stencil use the same card.html partial for product listings. After adding lazy loading to the partial, every category page product grid gets lazy loading automatically. But the first row of products (typically 3-4 items) is above the fold. These need eager loading.
Stencil’s Handlebars templates support the {{#if @first}} helper inside {{#each}} loops. Use it to apply eager loading to the first batch.
{{#each products}}
{{#if @first}}
<img src="..." loading="eager" fetchpriority="high">
{{else}}
<img src="..." loading="lazy" decoding="async">
{{/if}}
{{/each}}
This ensures the first product image loads with high priority (improving LCP) while all subsequent images lazy-load as the user scrolls.
CMS Content Images
Images inserted through BigCommerce’s WYSIWYG editor on CMS pages don’t pass through Stencil templates. You can’t add loading="lazy" to them at the template level. Instead, add a small script through Script Manager that retroactively applies lazy loading to content images.
<script>
document.querySelectorAll('.page-content img').forEach(function(img) {
if (img.getBoundingClientRect().top > window.innerHeight) {
img.setAttribute('loading', 'lazy');
img.setAttribute('decoding', 'async');
}
});
</script>
This script checks each image’s position. If it’s below the current viewport, it applies lazy loading. Images already in view remain eager-loaded.
Core Web Vitals Thresholds and What BigCommerce Stores Actually Hit
Google defines three Core Web Vitals metrics with specific pass/fail thresholds. LCP must be under 2.5 seconds. INP must be under 200 milliseconds. CLS must be under 0.1. These thresholds apply to the 75th percentile of page loads over a 28-day rolling window in the Chrome User Experience Report (CrUX).
BigCommerce stores running unmodified Cornerstone themes typically pass CLS and fail LCP and INP. The default Cornerstone lighthouse score on mobile hovers around 45-55. That’s not a BigCommerce platform limitation. It’s a theme and configuration issue.
LCP: The Metric That Matters Most for Rankings
LCP measures the time until the largest visible content element finishes rendering. On BigCommerce homepages, the LCP element is almost always a hero image or banner. On product pages, it’s the main product image. On category pages, it can be the category banner or the first product card image.
Identify your LCP element with PageSpeed Insights. It highlights the LCP element in its diagnostic output. Once you know what it is, optimize that specific element. Preload it with <link rel="preload" as="image">. Serve it as WebP. Set explicit dimensions. Remove any JavaScript that governs its display (carousels, fade-in animations, or progressive image loading libraries).
INP: The New Responsiveness Metric
INP replaced First Input Delay (FID) in March 2024. It measures the delay between a user interaction (click, tap, keypress) and the browser’s visual response. A store with heavy JavaScript on the main thread will fail INP because the browser can’t respond to clicks while it’s busy executing scripts.
BigCommerce stores with five or more third-party scripts routinely fail INP on mobile. The fix is the same as the TBT reduction strategy: defer non-critical scripts, move analytics to web workers, and reduce the total JavaScript payload. Every Script Manager entry should be evaluated for its INP impact.
CLS: Layout Stability
CLS measures unexpected layout shifts during page load. BigCommerce stores most commonly trigger CLS from images without explicit dimensions, web fonts loading and causing text reflow, and dynamically injected banners or promotion bars.
Setting width and height attributes on every <img> tag in your Stencil templates prevents image-related CLS. The browser reserves space for the image before it loads. Using font-display: swap causes a small CLS from font swapping, but the tradeoff is worth it. Invisible text (FOIT) is a worse user experience than a brief text reflow (FOUT).
For promotion bars and announcement banners, reserve their space in CSS with a fixed min-height on the container. Don’t let the banner push content down after it loads. Set the height in the stylesheet, and the layout stays stable.
Frequently Asked Questions
How do I check if Akamai CDN is caching my BigCommerce pages?
Open Chrome DevTools, go to the Network tab, and reload your storefront page. Click the main document request and look for the X-BC-Cache-Status response header. A value of HIT means the page was served from Akamai’s edge cache. A value of MISS means it went to BigCommerce’s origin server. Monitor this header across your highest-traffic pages to ensure consistent cache hits. UTM parameters and query strings can cause unnecessary cache misses by creating unique cache keys for functionally identical pages.
Why is my BigCommerce store’s LCP so slow on mobile?
Mobile LCP on BigCommerce stores is typically caused by three factors. Large, unoptimized hero images that aren’t served in WebP format add seconds to load time. Render-blocking CSS from Stencil themes with unused component styles delays First Contentful Paint, which cascades into slower LCP. Third-party scripts executing on the main thread compete for bandwidth and processing time with your LCP element. Start by identifying your LCP element in PageSpeed Insights, then preload it, optimize its format and size, and defer non-critical scripts that compete for main thread resources.
Can I add async or defer attributes to scripts in BigCommerce Script Manager?
BigCommerce’s Script Manager UI does not provide options for async or defer attributes. Scripts added through Storefront > Script Manager execute synchronously by default. The workaround is wrapping your script’s code in setTimeout(function(){ ... }, 0), which yields execution to the browser’s next idle frame. For scripts that only need to run after user interaction (live chat widgets, feedback tools), use an event listener or Intersection Observer to load them on demand instead of on page load.
Does BigCommerce automatically convert images to WebP?
BigCommerce uses Akamai’s Image Manager to automatically convert JPEG and PNG images to WebP format when the requesting browser supports it. The browser signals support by sending Accept: image/webp in the request headers. This conversion is transparent and doesn’t change the image URL. However, SVG files are never converted, and PNG files with 8-bit alpha channel transparency can lose their transparent backgrounds during conversion. Images hosted on external domains (Cloudinary, S3, Imgix) bypass Akamai entirely and require manual WebP conversion.
What Core Web Vitals score should a BigCommerce store target?
Target all three Core Web Vitals at Google’s “Good” thresholds: LCP under 2.5 seconds, INP under 200 milliseconds, and CLS under 0.1. These are measured at the 75th percentile of real user data over 28 days. An unmodified Cornerstone theme typically scores 45-55 on mobile PageSpeed Insights. With the optimizations in this guide (CSS cleanup, script deferral, image optimization, font subsetting, and lazy loading), BigCommerce stores consistently reach 75-90 on mobile. Reaching 90+ requires aggressive third-party script reduction, which sometimes means removing marketing tools that add measurable JavaScript weight.