Page resource optimization is the systematic process of reducing the bytes, count, and priority of every file the browser loads — HTML, CSS, JavaScript, fonts, images, video, and third-party scripts. Done well, it can cut total page weight by 50–70% and shave seconds off Largest Contentful Paint (LCP) and Time to Interactive (TTI).
Think of it like packing for a trip. You don't roll your shirts tighter before deciding which ones to leave at home. Cut first, then compress what survives, then make sure the essentials are at the top of the bag.
Every byte your page serves has a cost — measured in time, battery, data plan, and ranking position. Page weight directly drives the chain that ends in user experience metrics:
Knowing where the bytes go tells you where to spend optimization effort. Based on HTTP Archive data, the typical page weight distribution looks like this:
| Resource type | Share of bytes | Primary lever |
|---|---|---|
| Images | 40–55% | Format (WebP/AVIF), srcset, lazy-load |
| JavaScript | 20–30% | Tree shake, code-split, defer |
| Video | 10–15% | Lazy-load, poster image, modern codec |
| Fonts | 5–10% | Subset, WOFF2, font-display: swap |
| CSS | 3–5% | Remove unused, inline critical |
| HTML | 2–4% | Compress (gzip/brotli) |
| Other (audio, fetch, etc.) | ~5% | Audit case by case |
Images and JavaScript together are 60–85% of the typical page. Optimizing them first delivers the biggest wins by a wide margin.
Pull them in this sequence. Reordering wastes effort — you don't want to compress a script you're about to delete.
srcset and sizesso mobile users don't download desktop-sized images. Subset fonts to ship only the glyphs your content actually uses.Optimization without measurement is guesswork. Run an audit first so you know which 20% of the page is causing 80% of the weight.
WebP is roughly 25–35% smaller than JPEG at equivalent quality; AVIF is roughly 50% smaller. Use <picture> with format fallbacks and srcset for size variants.
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img
src="hero.jpg"
alt="Product hero"
width="1200"
height="600"
srcset="hero-480.jpg 480w, hero-768.jpg 768w, hero-1200.jpg 1200w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 50vw, 1200px"
fetchpriority="high"
>
</picture>The Largest Contentful Paint element — usually the hero image — is the single byte that defines your LCP score. Tell the browser about it as early as possible.
<link
rel="preload"
as="image"
href="/hero-1200.webp"
imagesrcset="/hero-480.webp 480w, /hero-1200.webp 1200w"
imagesizes="(max-width: 480px) 100vw, 1200px"
>Native lazy-loading with loading="lazy" defers off-screen images and iframes until the user scrolls near them — at no JavaScript cost.
<img src="below-fold.jpg" alt="..." loading="lazy" width="800" height="600">
<iframe src="https://example.com/embed" loading="lazy" title="..."></iframe>Analytics, chat widgets, and tag managers should never block first paint. Use defer for scripts that depend on the DOM, async for fully independent ones.
<!-- Independent, can run any time -->
<script src="/analytics.js" async></script>
<!-- Needs the DOM, but not critical for first paint -->
<script src="/interactive.js" defer></script>Most font files ship glyphs for languages and weights you don't use. Subset to the characters and weights actually rendered, ship as WOFF2, and preload the critical face.
<link
rel="preload"
as="font"
type="font/woff2"
href="/fonts/inter-regular-subset.woff2"
crossorigin
>
<style>
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-regular-subset.woff2') format('woff2');
font-display: swap;
}
</style>Inline the styles needed to render above-the-fold content directly in the HTML <head>. Load the rest non-blockingly.
<style>/* critical above-the-fold rules, ~10 KB */</style>
<link
rel="preload"
href="/styles/main.css"
as="style"
onload="this.onload=null;this.rel='stylesheet'"
>
<noscript><link rel="stylesheet" href="/styles/main.css"></noscript>Brotli typically compresses HTML/CSS/JS 15–25% better than gzip. Most modern web servers and CDNs support it with a single config flag — verify it's on by checking Content-Encoding: br in response headers.
Every third-party tag adds bytes you don't control. Periodically question each one: is it producing measurable value? If not, remove it. For necessary ones, load them with async and a facade pattern (placeholder UI that loads the real widget on interaction).
Avoid auto-playing background videos on mobile, lazy-load embedded videos behind a poster image, and ship a modern codec (H.265 / AV1) when supported.
<video
poster="/video-poster.jpg"
preload="none"
controls
width="1280"
height="720"
>
<source src="/clip.av1.mp4" type="video/mp4; codecs=av01.0.05M.08">
<source src="/clip.h264.mp4" type="video/mp4">
</video>What's happening: The CMS uploaded a full-resolution camera JPEG without compression or responsive variants, and it's now the LCP element on every visit.
Fix: Generate WebP/AVIF variants at 480w, 768w, 1200w, and 1920w. Compress at quality 75–80. Wire them up via <picture> + srcset and preload the variant the LCP viewport will need.
What's happening: A single 200 KB stylesheet blocks first paint until it finishes downloading and parsing.
Fix: Extract critical CSS for above-the-fold layout (~10 KB) and inline it. Load the rest with rel="preload" as="style" + the swap-on-load trick so it doesn't block rendering.
What's happening: Custom fonts load late, and the page either shows invisible text (FOIT) or a jarring swap (FOUT) once they arrive.
Fix: Subset the font, serve as WOFF2, set font-display: swap, preload the critical face, and use size-adjust / ascent-override on a fallback so the swap doesn't cause layout shift.
What's happening: A tag manager pulls in analytics, A/B testing, chat, session recording, and more — adding 500+ KB and dozens of network requests on every page.
Fix: Audit each tag, remove the ones not actively used, and load the rest async. For heavy widgets (chat, video embeds), use a facade — a lightweight placeholder that loads the real script only when the user interacts with it.
Performance audits flag pages above 1.6 MB compressed. The HTTP Archive median is around 2.6–2.8 MB, but median is not a goal — most of those pages fail Core Web Vitals on real-user data. Target ≤ 1 MB compressed for content pages and ≤ 1.5 MB for app-like experiences.
For most sites, image optimization. Images are 40–55% of typical page bytes, and converting to modern formats with responsive sources commonly cuts image weight by 50–70% with no visible quality loss.
No. Preload the LCP image and one or two critical fonts — that's it. Over-preloading floods the network and can make LCP worse by competing with the actually-critical resources.
Use defer when the script needs the DOM and execution order matters (most app code). Use async for fully independent scripts (analytics, error reporting). Never block first paint with a synchronous <script> tag in the head.
Yes — directly. LCP is the time to render the largest visible element, which is gated by bytes and priority. INP is gated by main-thread work, which is gated by JavaScript size. CLS is gated by reserved space and font-swap behavior. Resource optimization moves all three.
Indirectly. Generative search systems preferentially cite pages that already rank well, and ranking depends on Core Web Vitals — which depend on resource weight. A bloated page quietly hurts both your ranking and your odds of being chosen as an AI citation.
Quarterly at minimum, plus after any major release or template change. Better: wire performance budgets into CI so a build fails when total JavaScript or image weight crosses a threshold.
Page resource optimization rewards discipline more than cleverness. Audit before you optimize, cut before you compress, compress before you prioritize, and prioritize last. Pull those four levers in order and a typical page sheds 50–70% of its weight without architectural changes.
Run a Greadme deep scan to see exactly which images, scripts, fonts, and third-party tags are inflating your pages — and fix each finding with an AI-generated patch or a one-click GitHub PR.