Site Speed & Core Web Vitals

This page is written for an IT or security lead evaluating whether the AXO tag will slow a site down. It covers exactly how the tag loads, its measured effect on Google's Core Web Vitals, how to verify that on your own pages, and the guardrails that keep it fast on every release. It is intended to be linked directly from an RFP response or vendor-review questionnaire.

The short answer

  • One <script defer> tag. It is never render-blocking and runs after your content has painted.
  • The core is ~35 KB Brotli over the wire (~40 KB gzip) — optional features load as separate small modules only where used — and its total main-thread CPU on load is around 32 ms.
  • Its contribution to layout shift (CLS) is 0.00 — personalization is rendered flicker-safe, not by shoving content around after paint.
  • If your content has already painted when the tag runs, the anti-flicker hold is skipped entirely — the tag never blanks content a visitor is already looking at. When it does hold, it holds only the personalized elements, never the page.
  • You can confirm all of this yourself in minutes with Lighthouse — no site traffic required (see “Verify it on your own site” below).

How the tag loads

Installation is a single line placed before </head>:

<script src="https://axoapp.ai/tag.js?site=YOUR_SITE_ID" defer></script>
  • Deferred, not blocking. The defer attribute means the browser keeps parsing and painting your HTML and downloads the tag in parallel, executing it only after the document is ready. It cannot delay First Contentful Paint or block your own scripts.
  • Small and compressed. The core tag — the part every page loads — is ~35 KB over the wire (Brotli; ~40 KB gzip). For comparison that is smaller than a single typical hero image, and it is cached after first load.
  • Optional features load on demand. Behavioral scoring, the personalization decision, and rendering all live in that core. Capabilities only some sites or pages use — the HIPAA-safe privacy proxy, third-party enrichment, and the dashboard’s authoring preview — are split into separate small modules the core fetches at runtime, from the same first-party origin, only when they are actually needed. So a page never downloads code for a feature it doesn’t run, and the core stays lean as the platform grows.
  • One origin, no redirects. It loads directly from axoapp.ai over HTTP/2+ and calls back to that same origin. There is no chain of redirects or third-party loaders on the critical path.
  • No work before your content. Behavioral scoring and the personalization decision run client-side after paint (sub-200 ms), so they never sit in front of your Largest Contentful Paint.

Measured Core Web Vitals impact

Below is a lab (synthetic) trace of a live site running the tag, captured in Chrome. We show two cases because the tag behaves differently when it has nothing to personalize versus when a personalized region is held until the decision resolves (see the anti-flicker section). Absolute load times depend on the host page and the test device/network — the figures to focus on are the tag's own additions: its CPU time and its layout-shift contribution.

ScenarioLCPCLSTag main-thread CPU
No variant to render (anti-flicker dormant)0.48 s0.00~32 ms
Variant rendered (personalization + anti-flicker active)0.83 s0.00~32 ms

Both LCP figures sit well inside Google's “good” threshold (< 2.5 s), and the tag adds zero cumulative layout shift in both cases. For a returning visitor the tag briefly holds the specific personalized region (only that region) until the decision resolves, which adds a small amount of render time to that one element — never to the rest of the page, and always capped by a safety timeout (below). The numbers above are from one representative site in an unthrottled lab run; your own results will reflect your page, which is exactly why we recommend you measure it yourself.

Why it doesn't shift your layout (anti-flicker by design)

“Flicker” — the original content flashing before a personalized version replaces it — is the classic way personalization tools hurt CLS and LCP. AXO avoids it structurally:

  • Per-zone, not whole-page. Only the specific elements that will be personalized are briefly held; the rest of the page paints immediately. We never blank the <body>.
  • Never after paint. The tag checks the browser's paint timing before hiding anything. If a first contentful paint has already happened, the hold is skipped — content that is on screen stays on screen. Crawlers are never held either.
  • Configurable. The hold strategy is a per-site setting: always (the default, matching Adobe Target / Optimizely / VWO), only for visitors with an existing AXO cookie, or never.
  • Hard safety timeout. If a decision is ever slow, the held region reveals itself automatically within a few seconds no matter what, so a stalled request can never leave content hidden.
  • Swap-in-place. Personalized content occupies the same layout box as the original, so there is no reflow when it appears — hence the 0.00 CLS.

Reading PageSpeed Insights correctly: field vs lab data

A common point of confusion: pagespeed.web.dev shows two different things, and one of them will look empty on a low-traffic site.

  • Field Data (CrUX) is measured from real Chrome users and requires your site to have enough traffic to be included in Google's public dataset. A new or low-volume site shows “no data” here — that is expected and is not a problem with the tag.
  • Lab Data (Lighthouse) is a fresh synthetic audit Google runs on demand, in a controlled browser, every time you click Analyze. It needs zero traffic and is the authoritative way to judge whether a script slows the page.

So you do not need to wait for traffic to be confident — the lab run gives you the answer immediately.

Verify it on your own site

  1. Open pagespeed.web.dev (or Chrome DevTools → Lighthouse, or the lighthouse CLI) and run a report on a page that has the tag installed. Read the Lab/Lighthouse section; ignore that Field Data may be empty.
  2. For the strictest comparison, run it twice — once on a page with no active zones, and once on a page where a variant is live for the audience Lighthouse falls into (or use the extension's preview). The second run exercises the personalization + anti-flicker path.
  3. To isolate the tag's exact cost, run Lighthouse with and without the tag line and compare. The difference is AXO's contribution.
  4. What “good” looks like: CLS < 0.1 (the tag should add ~0), LCP < 2.5 s, and a small Total Blocking Time contribution from the tag.

How we keep it fast on every release

Performance is enforced in our build pipeline, not just measured once:

  • Bundle-size budget. Every change runs an automated check that fails the build if the compressed size of the core — or any on-demand module — exceeds its budget (the core is currently ~40 KB gzip / ~35 KB Brotli). Neither the core nor any feature module can silently get heavier.
  • Lighthouse budgets. A synthetic Lighthouse audit with Core Web Vitals budgets can be run against any deployed page on demand; cumulative layout shift is a hard gate.
  • Caching. The tag is served with a short browser cache plus stale-while-revalidate, so repeat visits within the window pay no download cost while a new release reaches every page within about a minute. The snippet on the Install page carries a &v= cache-busting parameter; it identifies the build the snippet was copied against and does not freeze your site on that version.

For reviewers: the tag is a single deferred, first-party core script of ~35 KB (plus small feature modules it loads on demand, only where used) that adds ~32 ms of main-thread work and 0.00 layout shift, runs its logic after your content paints, and is held to a size budget on every release. It is designed to be invisible to your Core Web Vitals, and that is verifiable synthetically without any traffic.