The image was ready. The page was not.
Start with the largest element on the mobile run: the hero image, a poster frame behind the video carousel. Lighthouse reports its LCP as 29.5 s, a figure simulated for a slow phone on a slow network. It also splits the time to draw that element into phases, taken from the load it actually observed:
| Phase (mobile, original) | Time |
|---|---|
| Time to first byte | 17 ms |
| Resource load delay | 73 ms |
| Resource load duration | 33 ms |
| Element render delay | 5,439 ms |
The server answered in 17 milliseconds and the image had finished downloading about 120 ms into the load. Then it waited 5.4 seconds to be drawn, 98% of the observed time to LCP. The network is not the issue on this page, and the image is not either. Here is how it is marked up:
<picture>
<source srcSet="…" type="image/webp" media="(max-width: 743px)" width="1128" height="2004"/>
<source srcSet="…" media="(max-width: 743px)" width="1128" height="2004"/>
<source srcSet="…" type="image/webp" width="4096" height="2560"/>
<source srcSet="…" type="image/jpeg" width="4096" height="2560"/>
<img class="dynamic-background__poster" src="…/plants-desktop-poster-500w.jpg" alt="plants"
loading="eager" width="4096" height="2560" decoding="async" fetchpriority="high"/>
</picture>
A phone-shaped crop for phones, WebP with a JPEG fallback, explicit dimensions,
fetchpriority="high", no lazy-loading on the one image that must not be lazy. This is the
markup every LCP guide asks for, and it is right here in the server’s HTML where the browser
can find it early. Whatever holds the hero back, it is not the image.
What stands between the image and the screen is in the head. Seven external scripts load there
with neither async nor defer: a polyfill, an error reporter, a CSS runtime, a date library,
an account module, a promo banner and an A/B testing loader, 218,151 bytes in total, and the
parser stops at each one. Two more stylesheets are linked from inside <body>. On mobile, first
contentful paint lands at 8.7 s.
A carousel of five clips, downloaded in every format
The hero rotates through five short clips. In the HTML, all five video elements look like this:
<video class="dynamic-background__video"
autoplay="" loop="" muted="" playsinline="" preload="auto">
autoplay and preload="auto" on all five, although only one slide is visible at a time. The
first carries its sources in the markup:
<source src="…/sept-2026/plants-desktop.webm" type="video/webm" media="(min-width: 744px)"/>
<source src="…/sept-2026/plants-mobile.webm" type="video/webm"/>
<source src="…/sept-2026/plants-desktop.mp4" type="video/mp4" media="(min-width: 744px)"/>
<source src="…/sept-2026/plants-mobile.mp4" type="video/mp4"/>
That list is well built. A browser walks it top to bottom and plays the first source that matches: the desktop WebM on a wide screen, the mobile WebM on a phone. One file per clip. The other four video elements arrive empty, and their sources are filled in by script.
Here is what the browser actually downloaded:
| Run | Video files | Video bytes | One file per clip would be |
|---|---|---|---|
| Mobile | 9 | 2.8 MB | 1.1 MB |
| Desktop | 17 | 7.6 MB | 1.6 MB |
On the phone, four of the five clips came down twice, once as WebM and once as MP4. The MP4 copies alone are 1.7 MB, 20% of everything the page transferred. On the desktop it is stranger: four clips came down in all four encodings, desktop and mobile, WebM and MP4. Video is 60% of the desktop page’s 12.8 MB, and 6.1 MB of it is files the markup above would never have selected.
These are whole files, not probes. The CDN reports the size of one of them, and it matches the transfer in the trace:
$ curl -sI https://media-www.sqspcdn.com/images/pages/homepage-2025/hero/videos/sept-2026/tutor-desktop.mp4
HTTP/1.1 200 OK
Content-Length: 1263693
Cache-Control: public, max-age=3024000
Accept-Ranges: bytes
We did not reverse-engineer the carousel script, so we can’t tell you which line requests the second format. What we can tell you is where to look: the HTML asks for one file per clip, so every extra copy is requested by code that runs after it.
1.2 MB of tags that Lighthouse files under the site itself
The Google Tag Manager snippet in the head is the standard one, with one change: it loads from the site’s own domain.
j.src = '/metrics/?id=' + i + dl;
Serving tags first-party is a legitimate choice, and it has a side effect worth knowing about.
On the mobile run, seven script responses came from /metrics/ on www.squarespace.com, between
154 KB and 220 KB each, 1,232,031 bytes together, and 1,477 ms of main-thread time by
Lighthouse’s count. Because they come from the page’s own host, Lighthouse’s third-party summary
does not list them. That table puts Facebook first, at 245 KB. The largest tag cost on the
page is not in the table at all.
The rest of the page talks to 48 hosts besides www.squarespace.com, seven of them Squarespace’s own. Across our audit base the median is 12; the study is linked below.
The HTML is gzip, and brotli would take another third off
$ curl -s -D - -o /dev/null -H 'Accept-Encoding: gzip, br' https://www.squarespace.com/
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Content-Encoding: gzip
Content-Length: 88669
Age: 178730
Vary: Accept-Encoding
Two good things first: the document came from the edge cache (Age is about two days), and
the server answered the trace in 17 ms. The client offered brotli and got gzip. We compressed
the same document both ways:
| Bytes | Of original | |
|---|---|---|
| Uncompressed HTML | 616,473 | 100% |
| gzip −9 | 87,810 | 14.2% |
| brotli −11 | 55,486 | 9.0% |
That is 33,183 bytes, or 37% off the document, for a server setting. It is not something a Squarespace site owner can change: all 35 Squarespace sites in our compression study answered with gzip. It is a platform decision, and on a 616 KB document it is a large one.
Why 616 KB? The site navigation is 209,059 bytes of it, a third of the document, built
before the first word of the hero. Across the page, 226 srcset attributes add up to 163,077
bytes, a quarter of the document.
What we changed, and what we did not
| Metric (mobile) | Original | Through WebSpeed |
|---|---|---|
| PageSpeed score | 30 | 35 |
| First Contentful Paint | 8.7 s | 3.2 s |
| Speed Index | 17.6 s | 15.8 s |
| Largest Contentful Paint | 29.5 s | 17.1 s |
| Time to Interactive | 30.3 s | 30.9 s |
| Total Blocking Time | 1,338 ms | 1,355 ms |
| Metric (desktop) | Original | Through WebSpeed |
|---|---|---|
| PageSpeed score | 39 | 39 |
| First Contentful Paint | 0.9 s | 0.5 s |
| Speed Index | 7.5 s | 4.4 s |
| Largest Contentful Paint | 3.8 s | 4.2 s |
| Total Blocking Time | 1,688 ms | 2,059 ms |
What the proxy did is mostly one thing: the blocking scripts in the head stopped blocking the parser. First paint on mobile moved from 8.7 s to 3.2 s, and the hero’s render delay on the mobile trace fell from 5.4 s to 2.1 s.
The score barely followed. The page still downloads 2.8 MB of video on a phone and 7.6 MB on a desktop, still runs a 744,328-byte homepage bundle that costs 3.2 s of main thread on the mobile run, and still loads its tags. Through the proxy, all 192 requests of the original still arrive: nothing was dropped to make the number look better, which is also why the number did not move much. On desktop our runtime added 371 ms of blocking time and the largest element arrived 0.4 s later, and the score stayed exactly where it was.
Why this is not a Squarespace problem
It would be easy to read this as “Squarespace is slow”. The page does not support that. The hero image is marked up better than most hand-built sites manage, 68 of the 75 images are lazy-loaded and 74 carry their dimensions, the HTML comes from an edge cache in 17 ms, and the fonts are self-hosted on the platform’s own CDN rather than pulled from a font service. The video source list in the markup is correct.
The costs here come from what a marketing homepage accumulates: a carousel whose script fetches more than it shows, a tag setup that grew to over a megabyte, a navigation that ships its whole menu in the document. None of that is the platform a Squarespace customer builds on, and the study of 155 Squarespace homepages below shows what that platform’s own share of a page actually is.
Fix it yourself
None of this needs us. It needs someone to do it, test that nothing broke, and do it again after the next template change — which is the part we sell.
- Open the Network panel and filter by Media This is the whole diagnosis, and it takes a minute. Count the files against the clips you meant to show. On this page a phone fetched nine files for five clips and a desktop seventeen. The HTML alone would fetch one per clip, so the extra copies come from whatever script fills in the other four video elements, and that is the code to look at.
- Let only the visible slide load All five carousel videos carry autoplay and preload="auto", though only one is on screen at a time. Each slide already has a poster image. Keep the first clip as it is, give the other four preload="none" without autoplay, and start each one when its slide comes up. The first screen looks identical and stops sharing the connection with clips nobody is watching yet.
- Defer the head scripts that are not needed to draw Seven external scripts sit in the head with neither async nor defer, 218,151 bytes between them, and the parser stops at each one. Through the proxy, where these were deferred, first paint on mobile moved from 8.7 s to 3.2 s. Some of these may well be needed before first paint (an A/B testing script usually is, by design), so check each against what it does rather than deferring all seven blindly.
- Count your tags where Lighthouse cannot If your Google tags are served from your own domain, PageSpeed's third-party summary no longer lists them, and the largest script cost on the page can vanish from the one table people read. Filter the Network panel by your tag path instead. Here it was seven script responses and 1.2 MB, and a third-party table that put Facebook at the top with 245 KB.
- Trim what the navigation ships in the HTML The header alone is 209,059 bytes of a 616,473-byte document, and the page as a whole carries 226 srcset attributes adding up to 163,077 bytes. The document still arrives compressed and fast, so this is not the bottleneck, but every byte of it is parsed before the hero section is reached.
What we measured, and what got worse
PageSpeed Insights (Lighthouse, mobile and desktop) run against the live site
and against the same page served through the WebSpeed proxy, on
3 Oct 2026. Audit 4f28771ae88e86 — the numbers
on this page are frozen at that run and are not restated later.
Proxy response re-checked on 5 Oct 2026: the proxied URL returns the page itself, not a redirect back to the origin. That check exists because a redirect makes both halves of a before/after the same page, and the improvement pure noise.
What the page still loaded
The accelerated run made 192 requests against the original's 192, and transferred 8.6 MB against 8.4 MB. We print this because a score can rise for two very different reasons — the page got faster, or the page got less of itself — and across our audit base the second is common enough to be worth ruling out in the open.
What got worse or stayed the same
- The desktop score did not move: 39 without us, 39 with us. Speed Index improved (7.5 s → 4.4 s), but the largest element arrived later through the proxy (3.8 s → 4.2 s) and total blocking time grew from 1,688 ms to 2,059 ms. On this page our runtime adds main thread work to a main thread that was already the problem.
- The mobile score went from 30 to 35, and the page is still in the red. The two accelerated mobile runs scored 33 and 35, so read the gain as a few points rather than a precise five. Total blocking time (1,338 → 1,355 ms) and time to interactive (30.3 → 30.9 s) did not improve at all.
- We did not touch the video. The same nine files and 2.8 MB arrived on mobile through the proxy as without it, duplicates included. Which encoding a carousel downloads is decided by the site's own script, and rewriting that is not a proxy's call to make.
- The page got heavier through the proxy, 8.4 MB → 8.6 MB on mobile. Most of that is one file: served from our test domain, the site's tag setup also loaded a 210,682-byte gtm.js from googletagmanager.com, which the original run did not request. Tags behave differently off their home domain, and the accelerated run carries that cost.
- The 744,328-byte homepage bundle is the largest single script cost on the page (3.2 s of main thread on the mobile run) and it is still there. Nothing we do shortens it.
- One page, one audit, one day. This is a teardown, not a study, and since this homepage is custom-built, nothing here generalises to Squarespace sites as a class.