Reducing HTTP requests


Every extra HTTP request is another chance for a slow mobile connection to stall your page, another file for the browser to schedule, and another dependency that can break during peak traffic. For e-commerce, that shows up as slower product detail pages, weaker paid traffic ROI, and more checkout abandonment – especially on real-world cellular networks.

Reducing HTTP requests means lowering the number of separate resources the browser must fetch to render and use a page (HTML, CSS, JavaScript, images, fonts, API calls, and third-party tags). Fewer requests usually means less connection overhead and fewer "blocking" moments on the critical rendering path, but only if you reduce the right requests.

[% image "./img/http-requests-breakdown.png", "Stacked bar chart comparing request counts by resource type before and after optimization", "Create a publication-ready stacked bar chart, wide landscape, designed to be fully legible without scrolling. Two vertical stacked bars labeled 'Before (cold load)' and 'After (cold load)' on the x-axis. Y-axis labeled 'HTTP requests (count)' from 0 to 140 with ticks every 20. Each bar is stacked with these segments in this order from bottom to top: HTML, CSS, JavaScript, Fonts, Images, Third-party, API/XHR. Provide a legend at right with the same category names. The 'Before' bar totals exactly 128 requests with segment values: HTML 1, CSS 6, JavaScript 22, Fonts 6, Images 70, Third-party 18, API/XHR 5. The 'After' bar totals exactly 74 requests with segment values: HTML 1, CSS 3, JavaScript 14, Fonts 2, Images 44, Third-party 8, API/XHR 2. Use neutral colors for baseline categories, a distinct negative color role for third-party, and a distinct positive improvement emphasis by visually annotating the total reduction '128 -> 74' above the bars. Visual hierarchy: totals first, then third-party segment, then images segment. No icons, no illustrations, no people, no fake UI, no decorative shapes; only chart elements, labels, and a small annotation." , "(max-width: 1536px) 100vw, 1536px", page.url %] [A request breakdown makes it obvious where to focus: cutting a few third-party and render-critical assets usually beats chasing small late-loading items.]

What counts as an HTTP request

A request is any time the browser asks a server for a resource. On a typical page, that includes:

  • The main document (HTML)
  • CSS stylesheets
  • JavaScript files (including dynamically imported chunks)
  • Images (including thumbnails, carousels, and background images)
  • Fonts (each file is typically a request)
  • API calls (XHR/fetch), beacons, analytics hits
  • Requests triggered by third parties (ads, tags, A/B testing, chat, reviews)

It's easy to undercount because requests don't only come from your code. They come from your CMS theme, tag manager, personalization tools, and embedded widgets.

Requests are not all equal

A page with 120 requests can be fast if most requests are lazy-loaded and cached. A page with 40 requests can be slow if just 3 of them are render-blocking, on a slow origin, and pulled from three different domains.

The practical split to care about is:

  • Critical requests: must complete to show above-the-fold content and reach Largest Contentful Paint (LCP).
  • Early interactive requests: affect how soon the page responds well (often related to INP and main thread work).
  • Late/background requests: load after the user can already see and use the page.

The Website Owner's perspective: A lower "total requests" number is nice, but the business win happens when the requests before LCP go down, or when you remove third-party calls that compete with your product image and price rendering during the first seconds.

How tools "calculate" request count

Most speed tools count requests by reading the page's network activity during a test run and tallying each resource fetch. Two details matter when you compare numbers:

  1. Cold vs repeat loads

    • A cold load starts with an empty cache, so almost everything is fetched.
    • A repeat load benefits from browser caching and can show far fewer network requests.
  2. Network vs cache hits

    • Some tools include resources served from memory/disk cache in the "request count" but they won't show network time.
    • For optimization decisions, prioritize network requests on cold loads, because that's closest to new visitors and paid traffic.

If you're using PageVitals synthetic testing, the most actionable view is the Network request waterfall in the docs: /docs/features/network-request-waterfall/. It helps you see not just how many requests exist, but which ones block rendering and which ones are chained behind others.

When request count actually hurts

The harm from "too many requests" usually comes from one (or more) of these mechanics.

Connection overhead and new domains

Requests are cheapest when they reuse an existing connection to the same host. They get expensive when the page pulls from many hostnames, because each new hostname can require:

You can mitigate this with preconnect and DNS prefetch, but it's still often better to reduce the number of third-party domains involved in first render.

Render-blocking dependency chains

Some requests don't just add time – they gate progress.

Common examples:

  • A stylesheet request that blocks rendering until it arrives
  • A synchronous script in the document head that delays parsing and rendering
  • Fonts that block text rendering (depending on loading strategy)

This is why "reducing requests" overlaps heavily with:

A single blocking JS request can delay dozens of later requests if it injects them after execution.

[% image "./img/http-requests-waterfall-critical-path.png", "Simplified network waterfall highlighting render-blocking and chained requests", "Create a simplified network waterfall visualization, wide landscape. X-axis is time in seconds from 0.0 to 6.0 with tick marks every 0.5s. Y-axis lists exactly 14 request rows grouped by phase with subtle separators: Document, CSS, JS, Fonts, Images, Third-party. Each row has a horizontal bar representing download time, with a lighter pre-bar segment representing connection setup for new origins (DNS+TCP+TLS). Include three origins in a legend: 'shop.example.com' (first-party), 'cdn.example.com' (first-party CDN), 'thirdparty.example.net' (third-party). Use neutral color for first-party, a more attention-grabbing negative role color for third-party bars, and a distinct warning highlight (outline) for render-blocking resources. Mark these as render-blocking with a small label at bar end: 'blocks render'. Show dependencies with thin arrows: Document -> CSS1, Document -> JS1, JS1 -> 7 third-party requests starting around 2.5s, CSS1 -> Font1 and Font2. Ensure timing is consistent: Document completes at 0.9s; CSS1 runs 0.9-1.8s; JS1 runs 1.0-2.4s; Fonts 1.8-2.8s; hero image request begins at 1.9s and completes at 3.8s; third-party chain begins at 2.5s with 7 requests staggered and completing by 5.8s. Visual hierarchy: highlight the hero image and render-blocking CSS/JS first, then the late third-party burst. No icons, no people, no fake UI controls, no decorative shapes; only chart labels, bars, arrows, and a minimal legend." , "(max-width: 1536px) 100vw, 1536px", page.url %] [A waterfall shows why "fewer requests" isn't the whole story: render-blocking CSS/JS and dependency chains can delay the hero element and inflate LCP.]

Header and server processing overhead

Even on HTTP/2 or HTTP/3 performance, each request still has:

  • Request/response headers (cookies can be a big hidden cost)
  • Server work to route, authenticate, and respond
  • CDN/origin variability (CDN vs origin latency)

If your origin is under load, many small requests can amplify the problem.

Competition for bandwidth and priority

Browsers prioritize some resources over others, but priority is not perfect – especially with many third-party scripts and images competing early. Excess requests can delay the one you actually care about: your LCP element (often the hero image or main product image).

How many requests is "good" or "bad"

Use request count as a smoke alarm, not a score.

Here are practical, website-owner-friendly ranges for cold-load request counts on a typical mobile test:

Page typeHealthy rangeInvestigate if you exceedWhy it matters
Landing page40–80100+Paid traffic is sensitive to slow first render
Product detail page60–110130+Carousels + reviews + tags often explode requests
Collection/search results60–120150+Many thumbnails plus filters and analytics calls
Checkout step40–90110+JS-heavy flows hurt responsiveness and completion

Two additional benchmarks that are often more actionable than total requests:

  1. Third-party requests on the critical path

    • If you have more than ~5–10 third-party requests firing before LCP, you likely have a prioritization problem.
    • Start with third-party scripts and decide what can be deferred or removed.
  2. Number of unique hostnames

    • A page that hits 12+ hostnames on first load is often doing unnecessary work, even if total request count is moderate.

The Website Owner's perspective: Your goal is not "the fewest requests possible." Your goal is "the fewest requests required for revenue." If a script doesn't help users decide, add to cart, or complete checkout, it should not compete with the first render.

How website owners interpret changes

A drop in request count can mean very different things depending on which requests moved.

Good reductions (usually translate to speed)

  • Fewer render-blocking CSS files (often via consolidation + asset minification)
  • Fewer JS files in the head (often by using async vs defer)
  • Fewer font files (fewer families/weights, subsets, or variable fonts)
  • Fewer third-party tags loading early
  • Fewer redirect hops (each redirect is effectively an extra request)

These reductions often improve:

Neutral reductions (may not move Core Web Vitals)

  • Removing requests that only happen after the page is already interactive
  • Combining a set of late-loading images into fewer requests (rare in modern setups)
  • Switching file formats to reduce bytes (great for bandwidth, but request count stays similar)

These changes still matter for:

  • Bandwidth costs
  • Time on site for slower devices
  • Overall reliability

"Bad" reductions (can backfire)

  • Over-bundling JavaScript to reduce requests, creating one huge bundle that increases JS execution time and hurts INP
  • Inlining too much CSS/JS into HTML, bloating the document and slowing TTFB-to-render
  • Creating a "mega sprite" image that delays decoding and LCP

Request count is a lever; use it alongside:

How to reduce HTTP requests safely

This is the practical playbook that tends to produce real wins without breaking the site.

1) Start with the critical path inventory

Before you cut anything, answer:

  • What are the requests that happen before LCP?
  • Which ones are render-blocking?
  • Which ones come from third parties?
  • How many unique hostnames are hit early?

If you have a waterfall, you can usually identify 80% of the opportunity in 10 minutes. (In PageVitals, this is exactly what the /docs/features/network-request-waterfall/ view is for.)

2) Reduce third-party requests first

Third-party scripts commonly add:

  • Multiple chained JS requests
  • Extra DNS/TLS setup to new domains
  • Heavy main-thread work after download

High-impact actions:

  • Remove tags that aren't tied to measurable outcomes (or fire them after consent / after interaction).
  • Delay non-essential tools until after above-the-fold content is visible (above-the-fold optimization).
  • Prefer server-side integrations where possible (fewer browser requests, fewer long tasks).
  • Audit tag manager containers quarterly; they tend to accrete.

A practical rule: If a third-party script doesn't influence the first interaction or purchase decision, it shouldn't run before LCP.

3) Consolidate CSS, but keep it critical

CSS can block rendering, so the win is often not "less CSS," but "less blocking CSS."

Options (often combined):

Be conservative with inlining: it reduces a request but can increase HTML size and delay first paint if overdone.

4) Treat JavaScript as requests and CPU

JavaScript is the most common source of "death by a thousand requests," especially with modern bundlers generating many chunks.

Use this decision guide:

GoalLikely tacticWatch out for
Faster first renderCode splitting + defer non-criticalToo many tiny chunks can create dependency delays
Fewer requestsBundle more aggressivelyLarge bundles increase parse/execute time and hurt INP
Better stabilityRemove duplicate librariesBreakage if different parts expect different versions

Key techniques:

[% image "./img/http-requests-bundling-vs-splitting.png", "Tradeoff chart showing fewer requests versus higher JavaScript execution cost", "Create a clean scatter plot with quadrant shading. X-axis labeled 'Initial JS requests (count)' from 0 to 30. Y-axis labeled 'Main-thread JS cost (relative)' from 0 to 100 with ticks every 20. Plot three labeled clusters: 'Over-bundled' at x=4, y=85; 'Over-split' at x=26, y=55; 'Balanced' at x=12, y=35. Add faint trend annotations: a horizontal arrow from 'Over-bundled' to 'Balanced' labeled 'split non-critical code'; a vertical arrow from 'Over-split' to 'Balanced' labeled 'merge tiny chunks'. Shade the bottom-left quadrant lightly as 'preferred zone'. Use neutral points for clusters, highlight 'Balanced' with a positive role color and a thicker outline. No icons, no illustrations, no fake UI controls, no decorative shapes; only axes, points, labels, arrows, quadrant label, and a minimal legend." , "(max-width: 1536px) 100vw, 1536px", page.url %] [Reducing requests by bundling can hurt responsiveness; the best outcome is usually a balanced middle with fewer critical chunks and lower main-thread cost.]

5) Cut font requests aggressively

Fonts are a common source of "surprise" requests: each weight, style, and subset can be another file.

High-leverage reductions:

  • Use fewer families and weights
  • Prefer variable fonts (often fewer files)
  • Subset to needed characters
  • Preload the one font that matters for above-the-fold text (preload)
  • Ensure caching is long-lived (Cache-Control headers and cache TTL)

Fonts often affect perceived speed even when metrics look "okay," because they influence what users see first.

6) Reduce image requests where it matters

You usually don't want to "combine images" in 2025, but you do want to avoid loading images before they're needed.

Practical request reducers:

  • Lazy-load below-the-fold images (lazy loading)
  • Avoid loading every carousel image up front; load the first, lazy-load the rest
  • Use responsive images to prevent fetching multiple candidates (responsive images)
  • Keep thumbnails sane on listing pages

Also reduce bytes so the remaining image requests finish sooner:

7) Improve reuse with caching and stable URLs

Request reduction isn't only about first load. A lot of business value comes from repeat visits, PDP-to-PDP navigation, and checkout flows.

To reduce repeat-load network requests:

8) Reduce "invisible" requests: redirects and duplicates

These are common and often overlooked:

  • Redirects: HTTP → HTTPS, non-www → www, trailing slash normalization, geo redirects. Each hop is extra latency and usually another request.
  • Duplicate resources: loading the same font from two sources, multiple analytics libraries, duplicated polyfills, duplicated frameworks across microfrontends.

A quick win is to grep your HTML for repeated includes and then confirm in the waterfall.

How to track improvements without chasing noise

Reducing requests is not a one-time project. Themes change, campaigns add tags, and plugins accumulate.

Use budgets that reflect business risk

A simple and effective approach:

  • Budget total cold-load requests for key templates (home, PDP, collection, checkout)
  • Budget third-party request count separately
  • Budget unique hostnames (especially before LCP)

This aligns with the broader idea of performance budgets. If you want to enforce budgets automatically in a workflow, PageVitals documents this under /docs/features/budgets/ and CI/CD options like /docs/ci-cd/github-action/.

Compare lab results with field behavior

Synthetic tests are great for catching regressions and understanding waterfalls. Field data tells you if real users improved.

To avoid misreading results:

  • Use consistent test settings (device/network) when comparing runs
  • Track both request count and the outcomes that matter (LCP/INP/CLS)
  • Sanity-check against field sources like CrUX data and understand field vs lab data

The Website Owner's perspective: Don't let teams "game" request count. A reduction only matters if it protects revenue: faster LCP on PDPs, smoother interactions on filters and checkout, and fewer third-party outages during peak campaigns.

A practical reduction checklist (in priority order)

  1. Identify requests before LCP (waterfall).
  2. Remove or defer third-party tags that fire early.
  3. Fix render-blocking CSS/JS (render-blocking resources).
  4. Reduce font files and preload only what's essential.
  5. Lazy-load below-the-fold images and avoid preloading carousels.
  6. Consolidate/optimize JS with a balanced bundling strategy (code splitting).
  7. Enforce caching and stable URLs to cut repeat-load requests.
  8. Set budgets and monitor regressions continuously.

If you do just the first three steps well, you often cut request count substantially and improve the metrics that drive conversions.

Frequently asked questions

There is no single cutoff, but for mobile you should be suspicious when a cold load exceeds about 100 requests or when third parties exceed 15 to 25. More important than totals is the critical path: the requests needed before LCP and before the page becomes responsive.

Yes. HTTP/2 and HTTP/3 reduce some overhead by multiplexing, but each request still adds headers, server work, prioritization complexity, and often a dependency chain. Many small requests can also delay critical resources and increase main thread work once responses arrive.

Not automatically. Cutting non-critical requests may not move LCP or INP. You get real gains when you reduce or reorder requests on the critical rendering path, remove render-blocking CSS and JS, and limit third-party scripts that compete for bandwidth and CPU during the first few seconds.

Both can be right. Bundling can reduce request overhead, but oversized bundles often hurt INP and total CPU. Code splitting can speed up initial render, but too many tiny chunks add scheduling and dependency delays. Aim for fewer, larger critical chunks and lazy-load the rest.

Start with a network waterfall and sort by domain and initiator. Look for third-party tags, redirect chains, duplicate libraries, multiple font files, and large image carousels. Then focus on what loads before LCP. Removing one blocking script can beat removing 30 late-loading images.