This is a demo report. Want one for your site?

Analyze Your Site Free

Audit Results

https://woocommerce.com/

WordPress

Lighthouse Lab Data

Measured in a simulated environment. Values may differ from real user experience.
53
Performance Score
Largest Contentful Paint
Cumulative Layout Shift
0
Interaction to Next Paint
545 ms
Good (90–100)Needs Improvement (50–89)Poor (0–49)

Field Data — Mobile (Real Users)

Core Web Vitals Poor

Chrome UX Report — p75 values from real mobile user experiences over the last 28 days

Largest Contentful Paint (LCP)
2.2 sNeeds Improvement
79.9%
12.9%
Interaction to Next Paint (INP)
153 msNeeds Improvement
84.7%
12.6%
Cumulative Layout Shift (CLS)
0.00Needs Improvement
97.2%
Time to First Byte (TTFB)
903 msNeeds Improvement
70.6%
21.7%
GoodNeeds ImprovementPoor
~

Summary

Needs improvement

WooCommerce.com has all four CrUX field metrics in the "needs-improvement" (orange) zone — LCP at 2216ms, INP at 153ms, TTFB at 903ms, and FCP at 2022ms. The root causes are: (1) massive GTM container bloat with 32 paused tags, 95 unused variables, and duplicate trackers across GTM and inline code, (2) uncached HTML responses despite being a WordPress marketing page, (3) 9 render-blocking CSS files including full Font Awesome, and (4) 60+ images loaded eagerly on a single page. Lab TBT (1362ms) is much worse than field INP (153ms), suggesting real users have faster devices than Lighthouse's 4x CPU throttle simulates, but the orange metrics still indicate clear room for improvement.

Must do
  • 1Eliminate Duplicate Trackers — GA4, Facebook Pixel, Hotjar, and LinkedIn Are All Loaded Twice
  • 2Delete 32 Paused GTM Tags — They Still Add to Bundle Size and Main Thread Parsing
  • 3Reduce TTFB — WordPress HTML is Not Edge-Cached
  • 4Defer All Analytics and Third-Party Script Loading by 3 Seconds
  • 5Replace Font Awesome with Inline SVG — 37.5KB Render-Blocking CSS + Webfont Bloat
Can defer
  • Delete 95 Unused GTM Variables and 53 Orphaned Triggers
  • Convert Hero and Showcase Images from JPG/PNG to WebP with Responsive Srcset
  • Use Facade Pattern for Wistia Video Embed
  • Lazy-Load 50+ Below-the-Fold Images — Currently All Load Eagerly
Expected outcome

LCP: 2216ms → ~1500ms (from TTFB caching ~600ms + CSS consolidation ~200ms + Font Awesome removal ~200ms), INP: 153ms → ~100ms (from duplicate tracker removal + GTM cleanup + script deferral), CLS: 0.00 → 0.00 (no CLS issues detected), TTFB: 903ms → ~250ms (from full-page caching + CDN edge cache)

Recommendations

1highinpBoth

Eliminate Duplicate Trackers — GA4, Facebook Pixel, Hotjar, and LinkedIn Are All Loaded Twice

Every major tracker on this page is loaded both inline (hardcoded in the HTML) and through GTM container GTM-W64W8Q. This means each tracker's JS is downloaded, parsed, and executed twice, and each installs duplicate event listeners on document. The duplicates detected: - Google Analytics 4 (G-98K30SHWB2): inline gtag.js (167.8KB) + 9 GA4 tags in GTM - Facebook Pixel: inline fbevents.js (96.9KB) + 7 FB Pixel tags in GTM (SDK + event tags) - Hotjar (278703): inline hotjar-278703.js (7KB) + Custom HTML tag in GTM - LinkedIn Insight: inline insight.min.js (18.7KB) + 6 LinkedIn pixel tags in GTM Total wasted: ~290KB of duplicate JS plus doubled main thread execution. Each duplicate analytics script installs event listeners on document — every user click passes through all of them, directly degrading INP. Action: Keep trackers only in GTM for centralized management. Remove the inline implementations from your WordPress theme/plugin:
1Find and remove the inline GA4 gtag.js snippet — search your theme's header.php or a plugin settings page for gtag/js?id=G-98K30SHWB2. The GTM container already has a Google Tag and GA4 Event tags for this measurement ID.
2Find and remove the inline Facebook Pixel snippet — search for connect.facebook.net/en_US/fbevents.js. Note: most FB Pixel GTM tags are paused (see GTM cleanup recommendation), so re-enable the needed ones in GTM first.
3Find and remove the inline Hotjar snippet — search for hotjar-278703. The GTM tag Hotjar (loading static.hotjar.com/hotjar-) is already enabled.
4Find and remove the inline LinkedIn Insight snippet — search for snap.licdn.com/li.lms-analytics/insight.min.js. Note: all 6 LinkedIn GTM tags are paused — keep one enabled in GTM before removing inline code.
5Verify after changes: Chrome DevTools → Network → filter by each domain. Each tracker should appear once.
Google Tag Manager · GTM-W64W8Q4 tags
GA4 Event (G-98K30SHWB2) #2
gaawe· GTM: G-98K30SHWB2
KEEP
Hotjar
html· static.hotjar.com/hotjar-
KEEP
Facebook Pixel SDK
paused
MODIFY
Script: px.ads.linkedin.com #1
paused· px.ads.linkedin.com/collect
MODIFY
Expected impact
Eliminates ~290KB of duplicate JS downloads and halves main thread execution time for analytics. Expected INP improvement from p75 153ms → ~110ms on mobile. Also reduces LCP resource contention.
2highinpBoth

Delete 32 Paused GTM Tags — They Still Add to Bundle Size and Main Thread Parsing

Container GTM-W64W8Q has 32 paused tags out of 61 total. Paused tags are not free — they remain in the GTM container JS bundle, and the browser must download, parse, and evaluate their code even though they never fire. This is a classic case of tag accumulation from past campaigns and defunct vendors. The paused tags include: - 6 LinkedIn pixel tags (px.ads.linkedin.com) — likely from old campaigns with different conversion events - 3 Facebook Pixel event tags (AddToCart, InitiateCheckout, Purchase) — plus the FB SDK tag and 3 unidentifiable Lead tags - 3 TradeDesk/adsrvr.org tags — ad platform tracking - 2 Google Ads Conversion tags and 1 Remarketing tag (Conv ID: 878432221) - 3 GA4 Event tags — duplicate config/event tags - 2 Custom Tags (__cvt_5RM3Q) — standard and custom event variants - 1 Custom Tag (__bzi) — unknown vendor - 12 unidentifiable Custom HTML tags — paused with stripped code - 3 unidentifiable Facebook Pixel — Lead tags Steps:
1Open GTM container GTM-W64W8Q → Tags → filter Status: Paused
2For each paused tag: verify with your marketing team that the campaign/vendor is no longer active
3Delete (not just keep paused) confirmed dead tags
4For LinkedIn tags: match by Script/URL containing px.ads.linkedin.com/collect — there are 6 separate tags that likely tracked different conversion events for old campaigns
5For the 12 unidentifiable Custom HTML tags: filter by Type: Custom HTML + Status: Paused — review each tag's code to identify its purpose, then delete if obsolete
6Publish the container version after cleanup
Google Tag Manager · GTM-W64W8Q15 tags
Google Ads Remarketing (878432221)
paused· GTM: 878432221
DELETE
Google Ads Conversion (878432221) #1
paused· GTM: 878432221
DELETE
Google Ads Conversion (878432221) #3
paused· GTM: 878432221
DELETE
Custom Tag (__bzi)
paused
DELETE
GA4 Event (G-98K30SHWB2) #1
paused· GTM: G-98K30SHWB2
DELETE
GA4 Event (G-98K30SHWB2) #3
paused· GTM: G-98K30SHWB2
DELETE
GA4 Event — purchase
paused· GTM: G-98K30SHWB2
DELETE
Facebook Pixel — AddToCart
paused
DELETE
Facebook Pixel — InitiateCheckout
paused
DELETE
Facebook Pixel — Purchase
paused
DELETE
Custom Tag (__cvt_5RM3Q) — standard
paused
DELETE
Custom Tag (__cvt_5RM3Q) — custom
paused
DELETE
Script: px.ads.linkedin.com #1
paused· px.ads.linkedin.com/collect
DELETE
Script: js.adsrvr.org
paused· js.adsrvr.org/up_loader.1.1.0.js
DELETE
12 × Custom HTML (unidentifiable)
paused
DELETE
Expected impact
Reduces GTM container JS size by an estimated 30-40%, cutting ~150-200KB of parsed JS. Expected INP improvement of ~15-25ms from reduced main thread parsing time.
3highlcpBoth

Defer All Analytics and Third-Party Script Loading by 3 Seconds

The inline trackers currently compete with LCP-critical resources for bandwidth and main thread time. The waterfall shows 4 inline trackers loading eagerly: - GA4 gtag.js: 167.8KB - LinkedIn Insight: 18.7KB (loads at 4326ms but requires early DNS/connection) - Facebook Pixel: 96.9KB (loads at 4922ms) - Hotjar: 7.0KB (loads at 3262ms) After you consolidate these into GTM (see duplicate tracker recommendation), the single GTM container should be deferred by 3 seconds. Users who leave before the first screen renders will never convert — losing them from analytics is acceptable. Load GTM with a 3-second delay instead of immediately in <head>. This removes analytics from the critical rendering path entirely, freeing bandwidth and main thread for LCP resources (CSS, hero image).
Before
<script>(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.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-W64W8Q');</script>
After
<script>window.addEventListener('load', function() {
  setTimeout(function() {
    (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.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-W64W8Q');
  }, 3000);
});</script>
Expected impact
~0ms improvement
4highlcpBoth

Replace Font Awesome with Inline SVG — 37.5KB Render-Blocking CSS + Webfont Bloat

Request #2 loads all.min.css (37.5KB) — the full Font Awesome stylesheet containing styles for 1,600+ icons. This file is render-blocking (Highest priority, loaded at 1593ms) and delays first paint by 258ms on mobile. After the CSS loads, the browser must also download the Font Awesome webfont files (additional HTTP requests + ~150KB+ webfont). The WooCommerce.com homepage likely uses only 10-20 icons from Font Awesome (social icons, UI icons). Loading the entire library for a handful of icons is extremely wasteful. Steps:
1Identify which Font Awesome icons are actually used on the homepage — open DevTools → search for fa- classes in the DOM
2For each icon, get the SVG from [fontawesome.com/icons](https://fontawesome.com/icons) or the Font Awesome SVG files
3Replace the <i class="fa-..."> elements with inline <svg> elements for above-the-fold icons (logo area, nav)
4For below-the-fold icons: use an SVG sprite file loaded after paint
5Remove the Font Awesome CSS enqueue from your WordPress theme — in functions.php: wp_dequeue_style('font-awesome'); (or the handle used by your theme)
6Remove the Font Awesome CSS file reference from the theme entirely if no other page needs it
Before
<link rel="stylesheet" href="/wp-content/themes/woo/fonts/font-awesome/css/all.min.css?v=1.0">
<i class="fab fa-wordpress"></i>
After
<svg width="24" height="24" viewBox="0 0 512 512" aria-hidden="true"><path fill="currentColor" d="M256 8C119..."/></svg>
Expected impact
~0ms improvement
5highlcpBoth

Reduce TTFB — WordPress HTML is Not Edge-Cached

CrUX field TTFB is 903ms (needs-improvement). The lab waterfall confirms: HTML document takes 1548ms total (745ms connection + 219ms server processing + 1029ms download for 50.8KB). The server returns server: Kestrel with no visible caching headers (no Cache-Control, no X-Cache, no CDN cache status). This means every visitor request goes directly to the origin WordPress server — no edge caching of HTML. For a marketing homepage that changes at most a few times per week, this is unnecessary. Every request triggers PHP execution, database queries, and Liquid template rendering. Steps:
1Install a full-page caching plugin if not already present. WP Rocket is the most effective, or use the free WP Super Cache. In WP Rocket: Settings → Cache → Enable caching for mobile devices → ON. This serves pre-generated HTML files directly from disk, bypassing PHP.
2Set Cache-Control headers for the homepage HTML. Add to your nginx config (or Apache .htaccess):
3If using a CDN (Cloudflare, Fastly, or similar): enable HTML caching at the edge with a short TTL (5 minutes) and stale-while-revalidate for seamless revalidation.
4Verify: curl -w 'TTFB: %{time_starttransfer}s\n' -o /dev/null -s https://woocommerce.com/ — should be <300ms after caching is active.
Before
# No caching headers on HTML response
server: Kestrel
After
# nginx configuration for WordPress full-page cache
location / {
    # WP Rocket cache file path
    set $cache_uri $request_uri;
    
    # Skip cache for logged-in users, POST requests, query strings
    if ($request_method = POST) { set $cache_uri 'null'; }
    if ($query_string != '') { set $cache_uri 'null'; }
    if ($http_cookie ~* "wordpress_logged_in") { set $cache_uri 'null'; }
    
    # Serve cached HTML file directly
    try_files /wp-content/cache/wp-rocket/$http_host$cache_uri/index-https.html $uri $uri/ /index.php$is_args$args;
    
    # Cache-Control for CDN edge caching
    add_header Cache-Control "public, max-age=300, stale-while-revalidate=3600";
}
Expected impact
~0ms improvement
6highlcpMobile

Consolidate 9 Render-Blocking CSS Files into 2-3 Combined Files

Requests #2 through #10 are 9 separate render-blocking CSS files loaded at Highest priority. Even though they reuse the existing connection (no DNS/TLS overhead), each requires a separate HTTP round-trip for TTFB — and the waterfall shows 166-326ms TTFB per file. The browser cannot paint anything until all 9 are downloaded and parsed. Breakdown: - all.min.css (37.5KB) — Font Awesome (addressed separately) - Two concatenated bundles via _static/??-eJx... (1.4KB + 19.3KB + 30.3KB) — WordPress static file concatenation - cover/style.min.css (1.9KB), gallery/style.min.css (2.1KB) — Gutenberg block styles - frontend.css (508B) — product vendors plugin - Another concatenated bundle (1.8KB) - deferred-global.min.css (3.7KB) — theme CSS Many of these are tiny files (<2KB) that could be inlined or combined. Steps:
1Install Autoptimize or use WP Rocket's CSS optimization: Settings → File Optimization → Combine CSS files → ON
2For the tiny Gutenberg block CSS files (cover, gallery): if these blocks aren't used on the homepage, dequeue them in functions.php:
3The frontend.css (508B) from product-vendors plugin is half a kilobyte — should be inlined directly into the combined stylesheet or dequeued if not needed on the homepage.
Before
<!-- 9 separate render-blocking CSS files -->
<link rel="stylesheet" href="/wp-content/themes/woo/fonts/font-awesome/css/all.min.css">
<link rel="stylesheet" href="/_static/??-eJx9i0EK...">
<link rel="stylesheet" href="/wp-includes/blocks/cover/style.min.css">
<link rel="stylesheet" href="/wp-includes/blocks/gallery/style.min.css">
<link rel="stylesheet" href="/_static/??-eJyVjM0K...">
<link rel="stylesheet" href="/wp-content/plugins/wccom-product-vendors/assets/frontend.css">
<link rel="stylesheet" href="/_static/??-eJytj0EO...">
<link rel="stylesheet" href="/wp-content/themes/woo/dist/deferred-global.min.css">
<link rel="stylesheet" href="/_static/??-eJyVjEsO...">
After
<!-- Dequeue unused block styles in functions.php -->
<?php
add_action('wp_enqueue_scripts', function() {
    // Remove block styles not used on homepage
    if (is_front_page()) {
        wp_dequeue_style('wp-block-cover');
        wp_dequeue_style('wp-block-gallery');
        wp_dequeue_style('wccom-product-vendors-frontend');
    }
}, 100);
?>
Expected impact
~0ms improvement
7highinpBoth

Move Hotjar and Klaviyo Custom HTML Tags to Native GTM Templates with Delayed Triggers

Two high-impact Custom HTML tags are currently enabled and fire eagerly: - Hotjar — loads static.hotjar.com/hotjar- via Custom HTML. Hotjar installs event listeners on document for session recording, adding 200-400ms of main thread blocking and capturing every user click. - Klaviyo — loads static.klaviyo.com/klaviyo.js via Custom HTML. Email marketing widget that doesn't need to load until the user scrolls or after page load. - Custom HTML (woo.kliken.com/success) — loads Kliken marketing script eagerly. Custom HTML tags run synchronously in the main thread and are heavier than native GTM template equivalents. Native GTM templates are sandboxed, load asynchronously, and are optimized by GTM's execution engine. Steps:
1Hotjar: Replace the Custom HTML tag with the official Hotjar GTM template from the Community Template Gallery (GTM → Tags → New → search "Hotjar"). Configure with Site ID 278703.
2Klaviyo: Replace with the Klaviyo GTM template from the Community Template Gallery.
3For both tags: change the trigger from "All Pages" or "Page View" to a Timer Trigger with interval 5000ms and limit 1. This delays loading by 5 seconds, ensuring LCP and initial interactivity are not affected.
4Kliken tag: If this is only needed on specific pages (e.g., success/thank-you page), restrict its trigger to that URL pattern instead of all pages.
Google Tag Manager · GTM-W64W8Q4 tags
Hotjar
html· static.hotjar.com/hotjar-
MODIFY
Klaviyo
html· static.klaviyo.com/klaviyo.js
MODIFY
Custom HTML (woo.kliken.com/success)
html· woo.kliken.com/success
MODIFY
Cookie Script
html
MODIFY
Expected impact
Reduces main thread blocking by ~300-500ms by deferring session recording and marketing scripts. Expected INP improvement from p75 153ms → ~120ms on mobile.
8mediuminpBoth

Delete 95 Unused GTM Variables and 53 Orphaned Triggers

Container GTM-W64W8Q has 95 unused variables (out of 147 total) and 53 orphaned triggers (out of 76). These are variables and triggers created for past campaigns but no longer referenced by any active tag. Even unused variables add to the GTM container JS — each Custom JavaScript variable includes its function code in the bundle, and each Data Layer Variable adds lookup logic. Examples of unused variables that should be deleted: - _transaction_ids (Cookie), gaUserTypeShort (Cookie), gaUserTypeLong (Cookie) — legacy cookie variables, likely from old Universal Analytics setup - page_productSKU, page_productCategory, page_productType (Data Layer Variables) — product-related variables not used by any current tag - post_title, post_category, post_subCategory, post_contentID (Data Layer Variables) — content tracking variables with no consumer - Custom JavaScript #10 — unused JS execution on every page load - geolocation.region, interaction_name (Data Layer Variables) — no tag references these Steps:
1Open GTM container GTM-W64W8Q → Variables
2For each variable: click it → check "Where this variable is used" in the right panel
3If no tags or triggers reference it → delete
4Repeat for Triggers: open each orphaned trigger, verify no tags use it, delete
5After cleanup, publish a new container version
Expected impact
Reduces GTM container size by an estimated 40-60KB of parsed JS. Minor but cumulative INP improvement of ~5-10ms from reduced parsing and evaluation overhead.
9mediumlcpBoth

Convert Hero and Showcase Images from JPG/PNG to WebP with Responsive Srcset

The hero section loads two header images in JPG format, and the showcase section below loads dozens of PNG screenshots. All images are served as @2x variants regardless of device width: - woo-header-01-C@2x.jpg (153.3KB, LCP element) — the grüum store screenshot - woo-header-01-A@2x.jpg (186KB) — another header variant - Showcase PNGs: woo-block-theme-5@2x.png (280.7KB), coolhunter-1.png (372.7KB), atholl.png (310.3KB), lunch-1.png (305.5KB) — all far too large WebP typically provides 25-35% savings over JPG and 50-70% savings over PNG at equivalent quality. Steps:
1Install ShortPixel or Imagify WordPress plugin to auto-convert uploads to WebP
2Configure <picture> elements or WordPress plugin's WebP delivery for automatic format negotiation
3Add responsive srcset to hero images so mobile devices don't download @2x desktop sizes
Before
<img src="/wp-content/uploads/2025/01/woo-header-01-C@2x.jpg" alt="gruum store">
After
<picture>
  <source type="image/webp" 
    srcset="/wp-content/uploads/2025/01/woo-header-01-C-400w.webp 400w,
            /wp-content/uploads/2025/01/woo-header-01-C-768w.webp 768w,
            /wp-content/uploads/2025/01/woo-header-01-C@2x.webp 1536w"
    sizes="(max-width: 768px) 100vw, 768px">
  <img src="/wp-content/uploads/2025/01/woo-header-01-C@2x.jpg" 
    alt="gruum store" width="768" height="614">
</picture>
Expected impact
~0ms improvement
10mediumlcpBoth

Use Facade Pattern for Wistia Video Embed

Request #18 shows a Wistia video embed (fast.wistia.com/embed/medias/7i19o3jndu/swatch) that takes 4132ms to fully load on mobile — including 3029ms just for connection setup to a new third-party domain. The full Wistia player loads additional JS, CSS, and video assets that total hundreds of KB. This video appears below the fold but still competes for network bandwidth during page load. Steps:
1Replace the Wistia embed with a static thumbnail image and a play button overlay
2Load the actual Wistia player only when the user clicks play
3Wistia supports this natively via their [Popover embed type](https://wistia.com/support/developers/popover) or you can implement a custom facade
Before
<script src="https://fast.wistia.com/embed/medias/7i19o3jndu.jsonp" async></script>
<script src="https://fast.wistia.com/assets/external/E-v1.js" async></script>
<div class="wistia_embed wistia_async_7i19o3jndu"></div>
After
<div class="wistia-facade" style="position:relative;cursor:pointer;background:url('/wp-content/uploads/wistia-thumb.webp') center/cover;aspect-ratio:16/9">
  <button aria-label="Play video" style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.3);border:none">
    <svg width="68" height="48" viewBox="0 0 68 48"><path d="M66.5 7.7s-.7-4.7-2.8-6.8C60.7-2 57.2-2 55.6-2.2 46.4-3 34-3 34-3s-12.4 0-21.6.8C10.8-2 7.3-2 4.3.9 2.2 3 1.5 7.7 1.5 7.7S.8 13.3.8 18.8v5.2c0 5.5.7 11 .7 11s.7 4.7 2.8 6.8c3 3 7 2.9 8.8 3.2C19.4 45.5 34 45.6 34 45.6s12.4 0 21.6-.8c1.6-.2 5.1-.2 8.1-3.1 2.1-2.1 2.8-6.8 2.8-6.8s.7-5.5.7-11v-5.2c0-5.5-.7-11.1-.7-11.1z" fill="red"/><path d="M27.2 13.5l18.1 10.5-18.1 10.5z" fill="#fff"/></svg>
  </button>
</div>
<script>
document.querySelector('.wistia-facade').addEventListener('click', function() {
  var s1 = document.createElement('script');
  s1.src = 'https://fast.wistia.com/embed/medias/7i19o3jndu.jsonp';
  var s2 = document.createElement('script');
  s2.src = 'https://fast.wistia.com/assets/external/E-v1.js';
  this.innerHTML = '<div class="wistia_embed wistia_async_7i19o3jndu" style="width:100%;aspect-ratio:16/9"></div>';
  document.head.appendChild(s1);
  document.head.appendChild(s2);
}, {once: true});
</script>
Expected impact
~0ms improvement

This is a demo report. Want one for your site?

Analyze Your Site Free
Woocommerce — CWV Audit Report | FixMyCWV