How to Reduce HTTP Requests on Your Website
Count the files your page loads, cut the ones you do not need, and see how our free report scores the result.
Free to use 18 min read Updated Oct 2026
Reduce HTTP requests in one minute
Quick answer: An HTTP request is your browser asking a server for one file, so to reduce HTTP requests you make the page ask for fewer files. You remove what you don't use, merge or inline small files, and delay anything the first screen doesn't need. Our report counts the elements in your finished HTML that point to a file, and it passes the check at 50 or fewer.
- Pass: 50 or fewer counted elements, and the report shows no How to Fix button.
- Fail: 51 to 100 counted elements is a warning and more than 100 is failed, and both states show the red How to Fix button.
- Fix: Start with the plugins, tags and images you no longer use, because deleting a file is the one change with no trade off.
To minimize HTTP requests without breaking anything, work in this order: remove first, then merge and replace, then delay, then cache, and stop as soon as the page feels fast. The count is a clue, not the goal. Our report reads the finished HTML instead of watching live network traffic, so the number can differ from your browser's.
What are HTTP requests and which ones add up?
Every page view starts with one HTTP request for the HTML document. The browser reads that document from top to bottom, and each time it meets a reference to a stylesheet, script, image, font, frame or media file, it sends another request for it. The protocol's rules live in RFC 9110, but you don't need the RFC to see the effect. A web page is really a list of files, and the list is nearly always longer than the owner expects. Your HTTP request count is simply the length of that list, and it is the requests per page figure that speed reports quote.
The extra requests rarely come from the content you wrote yourself. Theme and plugin files add stylesheets and scripts. A row of social icons arrives as a dozen small images. Web fonts add one file per weight and style, an embedded video or map pulls in a whole frame with files of its own, and a chat widget often loads a second and third file once the first one runs. Analytics and a tag manager are the classic case, since one line in your head section can bring in several more files from other hosts.
Add them up: one HTML file, 6 stylesheets, 12 scripts, 30 images, 4 fonts and 8 third-party files come to 61 requests, and none of those numbers is unusual. The Web Almanac request statistics from the HTTP Archive put the median page at 71 requests on desktop and 66 on mobile, according to the 2024 edition. An ordinary page therefore already lands in our warning range of 51 to 100, which is common and doesn't mean your site is broken. Those figures are network requests, while our report counts elements, so the two are close but not identical.
Request count versus page weight
Count is how many files a page loads, and weight is how many bytes they add up to. One 3 MB hero image is a single request, yet it can slow a page far more than twenty tiny icons. The two problems often travel together, but the fixes differ: you remove or merge files to cut the count, and you compress or resize them to cut the bytes. If your real problem is bytes, read our page size guide first.
How SEO Site Checker counts HTTP requests
The HTTP Requests row sits in the speed group of the report. To fill it, our crawler opens your page in headless Chromium, waits for the load event (it stops waiting after 20 seconds) and reads the finished HTML. Then it counts elements, which is a different thing from counting network traffic.
Our rule: The report adds up every img src, audio src, video src, script src, link href, iframe src, source src, embed src and object data in the finished HTML. A total of 50 or fewer passes, 51 to 100 is a warning, and more than 100 is failed.
This row is a different measurement from the DOM size check. That check counts every element in the document, while this row counts only the ones that point to a file.
What the report counts
The nine tag and attribute pairs above are grouped by tag name, so the result tells you how many of the counted elements are images, scripts, link tags and so on. Signed in users can open the grouped URL list in the report, which is the quickest way to see which group is heaviest. Counting is literal: an img with a src and loading="lazy" is counted, and so is a data: URI image, while an img that holds its address only in data-src is not.
Every link tag with an href counts, not just stylesheets. Canonical, icon, preconnect, preload and alternate links all add to the total, even though several of them, such as canonical and alternate, never download anything. Some things are left out on purpose:
- The HTML document itself. The request for the page is not an element inside the page, so it never enters the total.
- Files that CSS pulls in. Background images and web fonts declared in a stylesheet are invisible to the count.
- Srcset candidates. Only the src attribute is read, so alternate images listed in srcset, or in a source tag that has no src, are not added.
- Late additions. Fetch and XHR calls, and anything a script inserts after the load event, never reach the finished HTML the report reads.
Pass, warning and failed
The passed state shows its own message and no How to Fix button, so there is nothing to click when you are under the line. The warning and failed states are handled alike: both show the same too many requests message and the same red How to Fix button. If the on-screen text quotes a different recommended maximum, the thresholds on this page (50 and 100) are the ones that decide the status. A pass only says the page points to 50 or fewer files. It does not measure load time, the protocol, or whether those files block rendering.
These lines are ours, not Google's, and the pass line is stricter than the Web Almanac median, so plenty of healthy sites land in the warning band.
Why your browser number can differ
The biggest cause is noscript. Our parser reads the contents of a noscript block as real tags and counts them, while a normal browser with JavaScript on never fetches them. On one WordPress test page the report counted 83 elements, the browser console selector counted 64, and all 19 of the difference sat inside noscript blocks.
Timing matters too. The report reads the HTML as it stands once the load event has fired, so an image that a script adds two seconds later, or only when a visitor scrolls, can appear in your Network panel without ever being in the HTML the report reads.
For a quick cross-check, run the console selector from the counting section and compare totals. A warm cache changes timing and transferred size, but not the number of requests.
Why do HTTP requests slow a page down?
A request is not just a download. For a file on a host the browser hasn't used yet, it first does a DNS lookup to find the server, then, on HTTP/1.1 and HTTP/2, opens a TCP connection (the TCP handshake) and completes a TLS handshake to secure it. Only then does it send the request, wait for the first byte of the answer, which tools report as time to first byte, and download the rest. That connection overhead is paid per host, not per page. Each of those steps usually costs a round trip, and latency on a phone connection makes round trip time expensive. HTTP/3 merges the transport and TLS handshakes, and a cached DNS answer skips the lookup.
That is why the first file from each new host costs the most. Later files from the same host can reuse the open connection, which is what keep-alive is for.
Under HTTP/1.1 a browser opens about six parallel connections per host, and each connection handles one request at a time. Anything beyond six waits in a queue, a problem known as head of line blocking. This is where the old advice to make fewer requests came from, and for that protocol it was sound.
The render path matters more than the raw count. Stylesheets and synchronous scripts in the head hold back the first paint, so three blocking files can hurt more than forty images that load after the page is already visible. The visible result is a later First Contentful Paint and Largest Contentful Paint, and one slow third party file can stall everything queued behind it. Our render blocking resources page covers which files do that and how to spot them.
Be careful with the SEO claim. Google has not named request count as a ranking signal. What it does measure is how the page feels to a visitor, through Core Web Vitals, and requests are one cause of a poor score among several. The page speed and SEO guide explains that link between speed and search in more detail.
HTTP/1.1, HTTP/2 and HTTP/3: do fewer requests still matter?
Yes, but less than they used to, and the advice you follow should depend on the protocol your server speaks. The table compares the three versions in terms that affect a site owner's decisions.
| Protocol | How requests travel | Effect of many small files | What to do |
|---|---|---|---|
| HTTP/1.1 | One request at a time on each connection, with about six connections per host | Files queue up, so every extra file can delay the ones behind it | Combining and inlining small files still pays off |
| HTTP/2 (RFC 9113) | Many requests share one connection (multiplexing), with header compression | Small files cost far less, though each request keeps some overhead | Remove files you don't need, and don't force everything into one bundle |
| HTTP/3 (RFC 9114) | The HTTP/2 model carried over QUIC, which runs on UDP | Similar to HTTP/2, with better recovery when a packet is lost | Same advice as HTTP/2, with the biggest gains on weak mobile networks |
On HTTP/2 the browser no longer waits in line, but it hasn't made requests free. Every request still carries headers and server work, a file from a new host still needs its own DNS lookup and handshakes, and a blocking stylesheet still delays the first paint. HTTP/3 keeps the same model but removes the TCP stall: on HTTP/2 a single lost packet can pause every stream on the connection, and QUIC confines the delay to the stream that lost it.
The takeaway is simple. Removing requests you don't need helps on every protocol. Merging all your code into one giant bundle mostly helps on HTTP/1.1, and on newer protocols it can hurt, because a single edit invalidates the whole cached file. To see which protocol a site uses, open the Network panel, right-click the column headers, switch on Protocol and look for h2 or h3. If you see http/1.1 on your own site, follow the how to enable HTTP/2 guide for Apache and Nginx.
How to count the HTTP requests your page makes
Take a baseline before you change anything, or you will never know which step helped. You need two numbers, the total your browser reports and the total in our report, from the same URL.
- Open a clean window. Use a private or incognito window so browser extensions don't inject their own requests, and sign out of the site so you see what a visitor sees.
- Open the Network panel. Press F12 or Ctrl+Shift+I in Chrome, choose Network, and tick Disable cache so timing and transferred size match a first visit.
- Reload and scroll. Reload the page, note the total, then scroll all the way down so lazy loaded images and embeds start to download. Anything a script adds when you scroll is not in the finished HTML the report reads, so keep both the before and the after number.
- Read the count. The status bar at the bottom shows the number of requests. Filter by type (JS, CSS, Img, Font, Doc) to see which kind dominates, and sort by the Domain column to spot third parties (right-click the column headers to switch on Domain and Protocol).
- Read the request waterfall. Look for long chains where one file triggers the next, and for many different domains.
- Mirror the report in the console. Paste the one line below into the Console tab and press Enter. It counts the same nine tag and attribute pairs as our report.
- Run the report. Run your URL through our free SEO report and open the HTTP Requests row. If you are signed in, the grouped list shows which tags account for the total.
document.querySelectorAll('img[src], audio[src], video[src], script[src], link[href], iframe[src], source[src], embed[src], object[data]').lengthThe console number is often a little lower than the report because of noscript content, and it can be higher if scripts added elements after the load event. Write both totals down with the date, then change one thing and measure again the same way.
How to reduce HTTP requests step by step
The order matters. Removing a request is free, merging has trade offs, and delaying changes how the page behaves, so to make fewer HTTP requests you work from the safest change to the riskiest: remove first, then merge and replace, then delay, then cache, trimming third-party tags and limiting fonts along the way. Change one thing at a time and re-count after each step, which tells you what actually moved the number.
Remove files you do not use
This step usually gives the largest return for the least risk. Look for unused plugins and theme add-ons, duplicate libraries (two copies of jQuery is a common find), old widgets, images that no longer appear and tags left behind by a tool you cancelled years ago. Filter the Network panel by JS and CSS and ask of each file what it is for. If nobody can answer, switch it off and see whether anything breaks. Link tags are counted too, so drop preconnect hints and icons for hosts you no longer use: a leftover preconnect costs a count and opens a connection nobody uses. Never remove canonical, alternate or hreflang links just to lower the count, because they matter for SEO.
Combine and inline small CSS and JavaScript
Aim for one stylesheet and one script per page where that makes sense, both minified. Our JavaScript minifier and CSS minifier shrink the code before you merge it. Keep defer on scripts after combining, so one larger file doesn't block the first paint.
Inline only a few kilobytes of critical CSS in the head, and load the rest later. The pattern below inlines the rules for the top of the page and loads the full stylesheet without blocking it. The noscript line keeps the page styled when scripts are off.
<head>
<style>
/* critical rules for the first screen only */
body { margin: 0; font-family: system-ui, sans-serif; }
.hero { min-height: 60vh; }
</style>
<link rel="stylesheet" href="/css/site.min.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/css/site.min.css"></noscript>
</head>This critical plus deferred pattern improves the first paint, but it is count-neutral, or one higher with the noscript fallback, because the full stylesheet link stays and the fallback adds a second link tag. Inlining a whole small stylesheet does remove a link element, but only a few kilobytes are worth inlining, because the inlined rules can't be cached separately. A strict Content Security Policy blocks the inline onload handler, so use a plain link tag there. Our inline CSS guide covers when it helps and when it hurts.
Replace small images with inline SVG or a sprite
Icons are the classic request wasters. A set of twenty social and feature icons can easily be twenty separate image files. Draw them as inline SVG instead, so they ship inside the HTML and cost no request at all. Icon fonts also cut the count to one file, but they load every glyph and need accessibility care, so inline SVG is the better default.
<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true">
<circle cx="10" cy="10" r="6" fill="none" stroke="currentColor" stroke-width="2"/>
<path d="M15 15l6 6" stroke="currentColor" stroke-width="2"/>
</svg>If you already have a sprite, or you must support a design built around one, MDN's guide to image sprites in CSS shows how to combine many small images into one file and reveal each part with background-position. Avoid base64 data URIs for anything but tiny files. They remove the request in the browser, but the report still counts a data: URI as an element, and the encoded text bloats your HTML.
Lazy load below the fold and load embeds on click
Add loading="lazy" to images and iframes that sit below the first screen, and never to the main image at the top, because that delays Largest Contentful Paint. Lazy loading postpones the download, but the element is still in your HTML with a src, so our report still counts it. The first load is faster in practice even though the HTML count stays the same.
Click to play placeholders are different, and they are worth setting up for video, map and chat embeds. Show a poster and a button, and write the real iframe src only after the visitor clicks. The Intersection Observer API can do the same on scroll. In the report this saves one counted element per iframe, and only if the poster is a CSS background on the button rather than an img with a src. The big win, often many more files than the iframe itself, shows up in the Network panel and in real load time, not in the HTTP Requests row.
Trim third-party tags
Third-party tags deserve their own audit. In the Network panel, sort by Domain and list every external host your page talks to. Google Tag Manager, analytics, tracking pixels, social share buttons, chat widgets and ad scripts all show up, and one tag often triggers requests to several more domains. Remove the ones with no clear job.
For those you keep, load chat, maps and social widgets only after a click or scroll, and run the rest as async scripts or with defer so they don't block the page. The web.dev article on loading third-party scripts efficiently walks through async, defer, resource hints and self hosting.
Limit web fonts
Every weight and style of a web font is a separate file, and Google Fonts can quietly add a stylesheet plus several font files on top. Stay with two families and a few weights at most, serve WOFF2, and set font-display: swap so text shows while the font loads. A system font stack costs no request at all and suits body text on most sites. Font files requested from CSS do not appear in the report, so only the Google Fonts stylesheet link and any preload links count.
Cache and serve through a CDN
Give versioned files a long Cache-Control lifetime and put a CDN in front of them. This doesn't lower the count in our report, because the page still points at the same files. What it removes is network time on repeat visits and distance for far-away visitors. Our CDN usage test guide explains how the report checks for a CDN.
Reduce HTTP requests in WordPress, Shopify, Wix and plain HTML
The principles are the same everywhere, but the levers differ: a hosted builder gives you little control, so the work there is mostly removal.
WordPress
Deactivate plugins one at a time and re-count after each, since a plugin you forgot about can add several stylesheets and scripts to every page. Use a script manager, or the settings of a free optimization plugin, so contact form, slider and map files load only on the pages that use them. Where a plugin loads files everywhere, dequeue its scripts and styles on the pages that don't need them. Free optimization plugins offer merge and delay options, which are worth testing on a staging copy first.
Then switch off what WordPress adds by default and many sites never use. Two of those are real files that the report counts: the block library stylesheet (handle wp-block-library), which a classic theme may need only on pages built with blocks, and the embed script (wp-embed.min.js), which only matters if you embed posts from other WordPress sites. WordPress has been moving block styles to on-demand loading, so check in the Network panel whether wp-block-library still loads as a separate file before you remove it. Paste the snippet below into your child theme's functions.php file after the opening PHP tag, or into a small plugin of your own.
<?php
add_action('wp_enqueue_scripts', function () {
// Classic themes only: keep the block styles on posts that use blocks.
if (!is_singular() || !has_blocks()) {
wp_dequeue_style('wp-block-library');
wp_dequeue_style('wp-block-library-theme');
}
// Deregister the embed script so it is not printed.
wp_deregister_script('wp-embed');
}, 100);Test after each line, since a block theme or a page that embeds another post can lose styling. Archive pages and the blog home page fail the singular test, so check them too, because excerpts with blocks and block widgets in your sidebar or footer can lose their styling there. Many older tutorials remove the emoji code, but on current WordPress that code is printed inline, so removing it does not change the counted total.
Shopify
Apps are the usual culprit. Uninstalling an app often leaves its code in your theme files, so open the theme editor and delete the snippets and script tags it left behind, and also switch off unused app embeds in the theme editor, since many current apps load their files from there. A typical find is a script tag in theme.liquid that still loads a file from an app you removed months ago. Keep the number of fonts and homepage sections down, since each section can bring its own stylesheet and script. Finally, check which lazy loading the theme already applies before you add anything of your own.
Wix and Squarespace
Control is limited on these builders, and you can't dequeue the platform's own files, so the realistic target is the warning band rather than a perfect score. On Wix, open the app list in your dashboard and remove apps you no longer use, then check the site's custom code and tracking settings for snippets left behind by tools you dropped. On Squarespace, look at the Code Injection settings (header and footer) for forgotten tracking snippets, and delete unused third party blocks and embeds from your pages. After that, cut animations and extra fonts, and keep custom code minimal.
Plain HTML on Apache or Nginx
Bundle your files with the build tool you already use, turn on HTTP/2, and set Cache-Control headers for versioned assets. The HTTP/2 setup guide walks through the server configuration for both Apache HTTP Server and Nginx.
Mistakes that make fewer requests backfire
Much of the advice online dates from the HTTP/1.1 era, and applying it blindly can leave you with a lower count and a slower site.
Warning: On HTTP/2 and HTTP/3, one huge bundle means one small edit invalidates the entire cached file, so every returning visitor downloads all of it again.
- Inlining too much. Large inlined files or base64 images make the HTML heavier, the inlined parts can't be cached on their own, and the report still counts data URIs.
- Lazy loading the hero. Anything on the first screen should load right away, otherwise Largest Contentful Paint gets worse.
- Counting only images. Link tags, fonts and third-party tags add up just as fast and are easier to overlook.
- Deleting without checking. Consent banners, order tracking and ad scripts all look removable until a sale goes unrecorded. Find out what a tag does before it goes.
- Testing once. Measure in a private window, logged out, more than once, because a logged in session with a warm cache shows a different list from the one a visitor and the report see.
- Chasing the number. A page with a few render blocking files can feel slower than a page with more files that are all deferred.
A quick checklist to get to 50 requests or fewer
Work through this list from top to bottom. The notes inside the items say which ones lower the report total and which only speed up real loading.
- Baseline noted. Write down the report total and the Network panel total, plus the date.
- Unused items removed. Plugins, apps, tags and images you don't use are gone, which lowers the report total.
- Code merged. CSS and JavaScript are combined where it helps, minified and deferred, which lowers the report total.
- Icons inline. Icons are inline SVG or one sprite instead of separate files, which lowers the report total.
- Embeds on click. Video, map and chat embeds load only after a click, with the poster as a CSS background. Each one removes a counted iframe and speeds up real loading.
- Fonts limited. Two families and a few weights, in WOFF2, which helps real loading but barely moves the report.
- Re-tested. Checked in a private window, then run again in the free report.
- Target met. 50 or fewer counted elements passes, and a simple page often lands well below that.
How to Fix
The short version, the same steps you see when you click How to Fix in your report.
Why this fails: Our report found more than 50 elements in your finished HTML that point to a file, and each one is a separate trip to a server. A total of 51 to 100 is a warning and more than 100 is failed. The count includes every link tag and anything inside noscript, so it can read higher than your browser.
How to fix it:
- Open the grouped list in the report (signed in) or the Network panel, and note the starting number.
- Remove plugins, apps, tags and images you no longer use.
- Merge and minify small CSS and JavaScript files, or inline a tiny stylesheet entirely.
- Swap small icon images for inline SVG or one sprite.
- Load video, map and chat embeds on click (with a CSS background poster), and lazy load images below the fold.
- Keep web fonts to two families.
To compare counts, paste this into the console:
document.querySelectorAll('img[src], audio[src], video[src], script[src], link[href], iframe[src], source[src], embed[src], object[data]').lengthThis speeds up the first paint but does not lower the report count:
<style>/* critical rules for the first screen */</style>
<link rel="stylesheet" href="/css/site.min.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/css/site.min.css"></noscript>On WordPress (classic themes only), this removes the embed script and block library stylesheet. Paste it into a child theme's functions.php or a small plugin, then check archive pages and widgets:
<?php
add_action('wp_enqueue_scripts', function () {
// Classic themes only. Block themes need these styles.
if (!is_singular() || !has_blocks()) {
wp_dequeue_style('wp-block-library');
wp_dequeue_style('wp-block-library-theme');
}
wp_deregister_script('wp-embed');
}, 100);- WordPress: deactivate plugins one at a time and load scripts only where needed.
- Shopify: uninstall unused apps and delete the code they left in theme files.
- Wix and Squarespace: remove unused apps and embeds.
- Plain HTML: bundle your files and enable HTTP/2.
Re-test: Clear the page and CDN cache, then run our free report again on the same URL and check the HTTP Requests row is lower (50 or fewer passes). Read loading third-party scripts efficiently for more.
Frequently Asked Questions
How many HTTP requests is too many?
Does the number of HTTP requests still matter with HTTP/2?
How do I check how many HTTP requests my page makes?
How do I reduce HTTP requests in WordPress?
What causes too many HTTP requests on a page?
Do fewer HTTP requests help SEO?
Does lazy loading reduce HTTP requests?
Are CSS sprites still worth using?
Does a CDN reduce the number of HTTP requests?
See how your site scores right now
Paste your address into the free report, then use the How to Fix button on anything that needs work.
Free to use. Results in under a minute for most sites.