Third-party scripts: the page speed you gave away

Third-party scripts are often your slowest code. You can measure what each one costs, decide what to keep, then load the rest on your terms. This guide shows how to do it with Lighthouse and your browser, and how to fix the usual culprits on a small site.

By , founder of Porteur · Updated 14 September 2026 · Markdown

Find what each script costs

Start with one URL that gets visits. For example, yourproduct.com/pricing.

  • Open pagespeed.web.dev, paste the URL, select Mobile.
  • Read the top field section. This is real user data from the Chrome User Experience Report over 28 days. It decides your Core Web Vitals pass or fail.
  • Scroll to Diagnose performance issues. This is a Lighthouse run on a simulated mid-range phone over throttled 4G. Scores vary between runs.
  • Find the audit named Reduce the impact of third-party code. Expand it. You will see each third-party origin and its main-thread time and transfer size.

Note which origins take main-thread time and block rendering. Tag them for deeper inspection in your browser later: analytics, chat, A/B testing, consent, embeds, fonts, social, ads.

Read Lighthouse’s third-party audit the right way

Lighthouse shows lab data from one simulated load. Use it to spot heavy scripts and long tasks. Do not treat the score as a ranking factor.

  • Check the main-thread time attributed to each third-party origin. This is time your JavaScript blocked the thread.
  • Look at Total Blocking Time. Lighthouse weights it 30 percent in the Performance score. Third-party scripts often cause long tasks here.
  • Open the View Treemap link. It shows the bundle weight by source. Third-party packages often sit in their own boxes.
  • Click each opportunity and diagnostic that names third parties. For example, Minimize third-party usage, Defer offscreen images if embeds pull iframes, Avoid enormous network payloads.

Then cross-check against field data. PageSpeed Insights will show the Core Web Vitals assessment from real users when there is enough traffic. Those figures win when they disagree with lab runs.

Use the Performance panel to see what blocks

  1. Record one slow load

    Open Chrome DevTools, Performance panel. Check Screenshots and Web Vitals. Throttle to Fast 3G or Slow 4G. Hit Record, hard-refresh the page, stop after it renders.

  2. Find long tasks

    In the Main track, look for red long-task bars over 50 ms. Click them. The Summary will name the function and script URL. Many will be third-party.

  3. Inspect interactions

    If your INP is poor, record a click on your pricing table. The Event Log shows handlers and time spent. Heavy analytics listeners and A/B tools often show up here.

  4. Map costs to origins

    In the Network panel, group by Domain. Sort by Size and Waterfall Start. Note every third-party origin and when it starts, blocks, or shifts layout.

You now have a list by cost, not by brand. That is how you decide what to keep.

The usual third-party inventory on a small site

  • Analytics: one tag. Often two. Old ones are still loading.
  • Chat: Intercom-style widgets. They parse JSON, load fonts and attach many listeners.
  • Consent: a banner and a vendor list. Many inject before content and shift layout.
  • Video embeds: a full YouTube or Vimeo iframe on the homepage.
  • Maps: a full Google Maps JavaScript API on /contact.
  • Fonts: Google Fonts CSS and multiple hosts. Each adds DNS and TLS handshakes.
  • A/B testing: a synchronous snippet that blocks rendering to prevent flicker.
  • Social buttons: share and like widgets that pull more scripts.
  • Ads: tags that add iframes and shifts above the fold.

Audit each one. If nobody uses it, remove it. If few use it, load it on interaction. If it must run, defer it and keep it small. Where possible, self-host assets.

Decide per script: remove, load on interaction, defer, self-host

  • Remove: a heatmap from last quarter, a second analytics tag, a legacy A/B tool. Delete the tag and the container rule.
  • Load on interaction: chat opens on click, video loads when Play is pressed, maps load after a user asks for directions.
  • Defer: analytics that do not need to block rendering. Load after load or use defer or type=module.
  • Self-host: fonts and small helper scripts. Avoid a DNS lookup, connection and TLS handshake to a third-party host.

Example, before: You load a YouTube iframe on your homepage hero. LCP is a poster inside the iframe and arrives late. After: a static image button replaces the iframe. The iframe loads after click. LCP is now your H1 text or a hero image with fetchpriority on it.

<!-- Defer analytics that are not needed for first paint -->
<script defer src="/scripts/analytics.js" data-api="/collect"></script>

<!-- Module scripts defer by default -->
<script type="module" src="/scripts/app.js"></script>

Facades for video and maps

A facade is a static shell that looks like the thing. You swap it for the real iframe on click. You serve less JavaScript, fewer connections and no layout shift until needed.

  1. Build the video facade

    Render a poster image, a play button and the video title. Use the watch URL to build a thumbnail URL at build time. Add width and height.

  2. Swap on click

    On click, replace the wrapper with the YouTube iframe embed URL with autoplay=1. Set loading=lazy on the iframe.

  3. Maps: use a static image first

    Show a static map image from a Static Maps service or a screenshot you host. Add a View map button. Load the JS API only on click.

The dimensions in the example below are typical, not measured. Set width and height to your own poster and video, so layout is stable.

<!-- Video facade example: sizes shown are typical, not measured -->
<a class="video" href="https://www.youtube.com/watch?v=ID" aria-label="Play video">
  <img src="/img/video-ID.jpg" width="640" height="360" alt="Video title" loading="lazy" decoding="async">
  <span class="play">Play</span>
</a>
<script type="module">
  document.querySelectorAll('.video').forEach(v => v.addEventListener('click', e => {
    e.preventDefault();
    const id = new URL(e.currentTarget.href).searchParams.get('v');
    const iframe = document.createElement('iframe');
    iframe.width = 640; iframe.height = 360; iframe.title = 'Video';
    iframe.src = `https://www.youtube.com/embed/${id}?autoplay=1`;
    iframe.loading = 'lazy';
    e.currentTarget.replaceWith(iframe);
  }));
</script>

A fixed page keeps the hero text as LCP, has no layout jumps when the facade swaps, and loads the iframe only after the click.

Fonts from third parties: costs and options

Fonts are a third party too when you link to a remote host. Each host adds a DNS lookup, a connection and a TLS handshake before the file. You also risk layout shift on swap.

  • Self-host WOFF2 files. Use a single variable font if you can.
  • Preload the face used above the fold: rel=preload as=font with crossorigin.
  • Use font-display: swap or optional.
  • Subset with unicode-range to ship fewer glyphs.
  • Match metrics with size-adjust and ascent-override to avoid CLS.
  • A system font stack costs nothing and is fine for UI copy.
<!-- Preload and swap a self-hosted font -->
<link rel="preload" as="font" type="font/woff2" href="/fonts/brand-regular.woff2" crossorigin>
<style>
@font-face { font-family: Brand; src: url('/fonts/brand-regular.woff2') format('woff2'); font-display: swap; }
html { font-family: Brand, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; }
</style>

Preconnect only what must load early

Preconnect opens the DNS, TCP and TLS to an origin early. Use it for origins you must hit in the first paint. Do not spam it. Each open connection has a cost.

  • Use <link rel=preconnect> for one analytics endpoint if it runs on load.
  • Preconnect your own CDN or image host if the hero image is there.
  • Do not preconnect chat, video or map hosts if you load them on interaction.
<!-- Preconnect examples -->
<link rel="preconnect" href="https://cdn.yourproduct.com" crossorigin>
<link rel="preconnect" href="https://analytics.yourproduct.com" crossorigin>

Tag managers: power with limits

A tag manager is only as light as the tags inside it. A small container with strict triggers is fine. A container that injects many vendors on every page is not.

  • Keep one container. Clean out paused and legacy tags.
  • Fire analytics after load on content pages. Fire conversions on the event only.
  • Do not use document.write or synchronous scripts in a tag manager.
  • Restrict vendor tags to the pages that need them, for example, chat on /support only.
  • Version and test in preview mode. Measure TBT before and after each change.

How third-party scripts hurt the three Core Web Vitals

  • Largest Contentful Paint: heavy third-party code can delay TTFB and render, or make the hero image load late. Do not lazy-load the hero. Use fetchpriority=high and a preload when the hero is a CSS background.
  • Cumulative Layout Shift: banners injected above content, widgets that resize, swapped web fonts with different metrics. Give media boxes width and height. Reserve space for notices. Use overlays instead of pushing content down.
  • Interaction to Next Paint: every click, tap and key press counts. Third-party scripts cause long tasks, heavy event handlers and hydration delays. Break up long tasks. Keep scripts few and small. Move work to a web worker when you can.

As of 2026, the Good thresholds are 2.5 s for LCP, 0.1 for CLS and 200 ms for INP. INP replaced First Input Delay in March 2024. PageSpeed Insights shows the field assessment when there is enough data for the URL or origin.

Apply the fixes on one page, then roll out

  1. Pick one target URL

    Use /pricing or / to start. It should have traffic so field data appears. If not, rely on lab tests and user timings for now.

  2. Remove and delay

    Delete unused vendors. Move chat and video to load on interaction. Defer analytics. Self-host fonts and small helper scripts.

  3. Preconnect the few that remain

    Open early connections only for origins that must load in the first paint. Drop the rest.

  4. Retest in Lighthouse

    Run PageSpeed Insights again. Compare main-thread time by third-party origin. Check TBT, LCP and CLS in the lab run.

  5. Record an interaction

    In DevTools, measure a click or type path. Aim for the slowest interaction under 200 ms. Break long tasks and cut listeners until you reach it.

  6. Watch field data

    Give it time. Field data uses the 75th percentile over 28 days. You should see the Core Web Vitals assessment move from Fail to Pass when users feel the gains.

A fixed page will start fast, stay stable and feel instant on click. Your hero asset appears without delay, the layout does not jump, and clicks paint the next frame quickly.

Questions

Sources

Check my site, free

Paste a URL to get a free thirty second read of your site, the searches around it and rivals on them, with three full findings you can act on next.

  • Free check, no card
  • Read-only, your own accounts
  • Readable by your agent

Read next