HTTP Request Count Optimization for 1C-Bitrix
Bitrix project audits regularly uncover pages with 180–250 HTTP requests on load. This is not an exaggeration: each component can add 2–4 CSS files and 3–5 JS files, plus icons as individual PNGs, plus analytics trackers, plus widgets. Every request involves DNS, TCP, TLS, and response headers. With HTTP/1.1, the browser maintains 6 parallel connections per domain. With HTTP/2, multiplexing helps, but does not eliminate overhead entirely.
Auditing the Current State
The first step is to measure, not guess. Tools:
- Chrome DevTools → Network: Requests, Transferred, DOMContentLoaded, Load columns
- WebPageTest (webpagetest.org): Waterfall diagram with grouping by type
- Lighthouse: "Serve static assets with an efficient cache policy" + "Avoid chaining critical requests" metrics
Look at the waterfall for the longest dependency chains. The typical picture:
HTML → CSS (×5) → fonts (×6) → JS (×8) → images (×40+) → component Ajax requests (×3-5)
CSS and JS: Combiner and Bundling
Bitrix Built-in Combiner
In the main module settings (/bitrix/admin/settings.php?lang=ru) there are "Combine CSS files" and "Combine JS files" options. The combiner merges files into a single request /bitrix/cache/css/[hash].css. It works, but with caveats:
- Only combines files from
AddCSS()/AddHeadScript(), not inline component styles - When the cache is invalidated (any file is edited), the hash changes — browsers re-download
- Does not minify CSS/JS — concatenation only
Webpack/Vite for Custom Assets
For your own code (not the Bitrix core), configure a bundler:
// vite.config.js
export default {
build: {
rollupOptions: {
input: {
main: 'local/templates/main/src/main.js',
catalog: 'local/templates/main/src/catalog.js',
}
}
}
}
You get 2 bundles instead of 15+ individual files. Splitting into main and catalog matters: do not load catalog JS on static pages.
Icons: Sprites and Inline SVG
Each icon as a separate file is the fastest way to accumulate 30–50 extra requests. Options:
SVG sprite — one file with all icons:
<!-- sprite.svg -->
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="icon-cart" viewBox="0 0 24 24">...</symbol>
<symbol id="icon-search" viewBox="0 0 24 24">...</symbol>
</svg>
<!-- usage -->
<svg><use href="/local/templates/main/img/sprite.svg#icon-cart"></use></svg>
One HTTP request for all site iconography. Cached for a long time.
Inline SVG via a PHP helper for above-the-fold icons:
function svg(string $name): string {
static $cache = [];
if (!isset($cache[$name])) {
$path = __DIR__ . '/icons/' . $name . '.svg';
$cache[$name] = file_exists($path) ? file_get_contents($path) : '';
}
return $cache[$name];
}
Inline SVG = 0 HTTP requests, but increases HTML size.
Component Ajax Requests
Bitrix components with 'AJAX_MODE' => 'Y' make a separate XHR when navigating between catalog pages. This is normal, but on page initialization there are often several parallel Ajax requests to ajax.php with different parameters. The solution is batching: combine multiple requests into one using a queue:
// Accumulate requests for 50 ms, then send a batch
const queue = [];
function batchRequest(params) {
queue.push(params);
if (queue.length === 1) {
setTimeout(() => {
const batch = [...queue];
queue.length = 0;
fetch('/api/batch', {
method: 'POST',
body: JSON.stringify(batch)
});
}, 50);
}
}
On the server side — an /api/batch endpoint that dispatches requests and returns an array of responses.
Images: Lazy Loading and Sprites
Catalog images are the largest source of requests. The minimum required:
// In the catalog.element component template
echo '<img src="' . $arItem['PREVIEW_PICTURE']['SRC'] . '"
loading="lazy"
width="' . $arItem['PREVIEW_PICTURE']['WIDTH'] . '"
height="' . $arItem['PREVIEW_PICTURE']['HEIGHT'] . '"
alt="' . htmlspecialchars($arItem['NAME']) . '">';
loading="lazy" — native lazy loading. The browser does not request images below the viewport until the user scrolls. On a catalog page with 48 cards, this removes 30–40 requests from the critical path.
Case Study: Marketplace Optimization
An electronics online store on Bitrix "E-Commerce": home page — 214 HTTP requests, Load time 8.3 s.
What was done step by step:
- Enabled CSS/JS combiner → -18 requests
- Built an SVG sprite from 64 icons → -63 requests
- Added
loading="lazy"to all below-fold images → -41 requests from the critical path - Moved Google Analytics and Yandex.Metrica to
defer→ removed from blocking path - Merged 5 custom JS files into 1 bundle via Vite → -4 requests
Result: 214 → 88 requests, Load time: 8.3 s → 3.1 s, LCP: 5.2 s → 1.9 s.
HTTP/2 Push and Preload
With HTTP/2, resources can be announced via the Link: <url>; rel=preload response header. In Nginx:
location = / {
add_header Link "</local/templates/main/fonts/roboto-400.woff2>; rel=preload; as=font; crossorigin";
add_header Link "</local/templates/main/css/main.css>; rel=preload; as=style";
}
This does not reduce the number of requests, but shortens the waterfall — the browser learns about resources earlier.
Timeline
| Scale | Scope | Duration |
|---|---|---|
| Basic | Combiner, lazy loading, defer for trackers | 1–2 days |
| Medium | SVG sprite, Vite bundling for custom code, HTTP/2 | 4–7 days |
| Full | Batch API for Ajax, full audit and elimination of all redundant requests | 8–14 days |







