The server is not the problem

Start with the response headers of the homepage:

$ curl -s -D - -o /dev/null -H 'Accept-Encoding: br' https://www.1c-bitrix.ru/

HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: no-cache
X-Bitrix-Composite: Nginx (files)
X-Accel-Expires: 7200
Content-Encoding: br

X-Bitrix-Composite: Nginx (files) means this HTML never reached PHP. Bitrix’s composite mode wrote the page to disk, and nginx handed the file over as if it were static. The lab trace agrees: time to first byte on the mobile run was 51 ms. The document is compressed with brotli, which is the right call and not one every platform makes.

The second thing is in the markup. All 33 external scripts on the page sit in the last 3% of the document, starting 194,405 bytes into a 200,048-byte file. The only scripts left in the head carry data-skip-moving="true", which is Bitrix’s opt-out from its own “move all JavaScript to the end of the page” setting. So that setting is on, and it shows: total blocking time on the mobile run was 40 ms.

Fast server, scripts out of the way, idle CPU. And then:

Metric (mobile)Original
Time to first byte (lab trace)51 ms
First Contentful Paint6.3 s
Largest Contentful Paint9.5 s
Total Blocking Time40 ms

Six seconds pass between a document that arrived almost instantly and the first thing a phone can draw. Caching made the page arrive sooner. It did not change what has to arrive before anything is painted.

Fourteen stylesheets before the first pixel

The head links fourteen stylesheets, and the browser will not paint until it has every one. Bitrix appends a version string to each URL, and that string is more useful than it looks:

/bitrix/content_fonts/opensans/800/opensans.css?1562746550373

It is two numbers glued together: the file’s modification time as a Unix timestamp, then its size in bytes. We checked on this file. The server answers Content-Length: 373 and Last-Modified: Wed, 10 Jul 2019 08:15:50 GMT, which is timestamp 1562746550. Anyone can read the uncompressed weight of a Bitrix page’s assets straight from its source.

Read that way, the head looks like this:

StylesheetBytes, uncompressed
app.min.css228,615
bootstrap.min.css162,023
main.css33,748
GDPR banner, base.min.css14,360
social login form, style.css7,102
styles.css4,551
owl.carousel.min.css3,351
GDPR cookie settings, style.min.css2,148
auth header form, style.min.css1,968
opensans.css × 5 (weights 300 to 800)349 to 373 each
14 files459,659

Compressed, all fourteen come to 77,777 bytes on the wire, so the problem is not their size. It is that there are fourteen of them, each one a request the first paint must wait for, and that none of them comes from the merged bundle Bitrix can build itself: a merged template stylesheet lives under /bitrix/cache/css/, and no link on this page points there.

Five of the fourteen are the font files. Each holds exactly one rule:

@font-face {
	font-family: 'OpenSans';
	src: url('OpenSans-ExtraBold.eot');
	src: url('OpenSans-ExtraBold.eot?#iefix') format('embedded-opentype'),
	url('OpenSans-ExtraBold.woff2') format('woff2'),
	url('OpenSans-ExtraBold.woff') format('woff'),
	url('OpenSans-ExtraBold.svg#open_sansextrabold') format('svg');
	font-weight: 800;
	font-style: normal;
	font-display: swap;
}

Five render-blocking requests that move 4,615 bytes to deliver 1,793 bytes of CSS. And this one, the 800 weight, is never used: the measured run downloaded the 300, 400, 600 and 700 WOFF2 files and no ExtraBold. Note what is right in the same file, though: font-display: swap, so text does not hide while the fonts load.

Lighthouse’s own estimate for removing the render-blocking requests on this run is 2.75 s of first contentful paint. That is an estimate, not a measurement; our measured number is further down.

Half the page is two images

The mobile run moved 3,195,882 bytes (3.0 MB, counting a megabyte as 1,024²). Here is the composition:

TypeRequestsBytes
Images372,289,646
Scripts38542,444
Fonts4211,834
Stylesheets1477,777
Documents257,291
Other916,890
Total1043,195,882

Images are 72% of it, and two files are 1,562,608 bytes, 49% of the whole page.

The first is winners.jpg, 827,981 bytes, a CSS background:

.company-count-info_background{background-image:url(/images/content_ru/background/winners.jpg);
  background-size:cover;background-position:50%;background-repeat:no-repeat}

On desktop that section is the largest contentful element of the page. On mobile it is not, but the stylesheet applies the background anyway, so the phone downloads it too.

The second is a thumbnail in the press-release slider:

<img class="press-realise__img"
     src="/upload/iblock/94d/xhmzrjgyng2jcumybi3vxol8rp94iwqt/orig.png"
     alt="ИИ‑агенты: от хайпа к практике. …">

The file is a 1000×1000 truecolour PNG of 734,627 bytes (we read its header, not the whole file). The stylesheet puts it into a box 10.6666666667em tall. The seven other thumbnails in the same slider weigh between 18,560 and 100,874 bytes, so this is not a pattern of the template. It is one upload that went in at the size it was exported.

None of the 18 img tags on the page carries a loading attribute, and 17 of them have no width and height. The press slider, the client logos and the country flags all load with the first screen. On a phone that means the 41 KB product image that becomes the largest contentful element shares the throttled connection with 1.5 MB of images further down the page.

The platform has the tool for this. core.min.js, which this page downloads, lists /bitrix/js/main/lazyload/dist/lazyload.bundle.js among the modules it already contains. It is just not attached to these images.

A realtime client for a visitor it does not serve

Near the end of the document, Bitrix’s push-and-pull client loads with its dependencies:

<script src="/bitrix/js/pull/protobuf/protobuf.js?1703578952274055"></script>
<script src="/bitrix/js/pull/protobuf/model.min.js?170357895214190"></script>
<script src="/bitrix/js/rest/client/rest.client.min.js?16121731009240"></script>
<script src="/bitrix/js/pull/client/pull.client.min.js?176053918649849"></script>

Read the trailing digits again: 274,055 + 14,190 + 9,240 + 49,849 = 347,334 bytes of JavaScript, 35% of the 978,931 bytes of script source this page links. It is the infrastructure for live updates: notifications, chat, presence. Right above the tags, the page prints the client’s configuration for this visitor:

(window.BX||top.BX).message({"pull_server_enabled":"Y", … ,"pull_guest_mode":"N","pull_guest_user_id":0, …});

Guest mode off, user zero. A visitor arriving from search is a guest. In the measured run none of these scripts made a request of its own; the resource list has no call they could have made.

We want to be careful about what this proves. A logged-in partner on the same URL may well use the channel, and from outside we cannot see that. For the anonymous first visit that PageSpeed measures, and that a new customer makes, it is 70,936 bytes on the wire and 339 KB to parse for a feature the page has switched off for them. The cost on this run was mostly bandwidth: blocking time was 40 ms, so the parse did not jam the main thread.

What changed through the proxy

MetricMobile, originalMobile, WebSpeedDesktop, originalDesktop, WebSpeed
PageSpeed score597574100
First Contentful Paint6.3 s2.6 s0.6 s0.3 s
Speed Index6.6 s4.6 s1.7 s0.6 s
Largest Contentful Paint9.5 s5.1 s2.9 s0.5 s
Total Blocking Time40 ms0 ms2 ms0 ms
Cumulative Layout Shift00.0020.1680.003

The largest single change is first paint on mobile, 6.3 s to 2.6 s, and it comes from the head: the accelerated run requests 5 stylesheets instead of 14. Images went from 37 requests and 2,289,646 bytes to 20 requests and 214,288 bytes on mobile. The four product box PNGs (211,187 bytes) became WebP files of 40,739 bytes together, and winners.jpg became 111,959 bytes. The 1000×1000 thumbnail is below the first screen and was never requested.

Composition, since a score that rises because a page stopped loading is not a win: 65 of 104 requests and 0.8 of 3.0 MB. The gap is lazy-loaded images below the fold and third-party scripts that wait for interaction, both listed in the limits above. The original HTML was the same 54,053 bytes for the mobile and the desktop run, and the largest element on mobile was the same product image before and after, so the mobile run measured the mobile page.

Two things went the wrong way, and one matters. Our accelerated run gave that mobile product image loading="lazy", which is exactly the hint a first-screen image should not get, and mobile LCP stopped at 5.1 s instead of going further. The other is a mobile layout shift of 0.002 where the original had none.

What Bitrix already gets right on this page

The measurement does not say Bitrix is slow. It says the opposite about the parts Bitrix owns: the HTML came from a file cache in 51 ms, it was brotli-compressed, every script was moved out of the head by a platform setting, and the font file we fetched declares swap. A CMS that does those four things by default is ahead of most hand-built sites we audit.

What remains is what an editor and a template author decide: fourteen stylesheets where the platform could merge them, an image uploaded at 1000 pixels into a slot a fraction of that, a background JPEG nobody re-encoded, and a module attached to every visitor rather than to the ones it serves. Each of those is a one-line or one-file change. None of them is reached by a cache, because a cache makes the same bytes arrive sooner; it does not decide which bytes should be there.

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.

  1. Let Bitrix merge the stylesheets, or merge the five font files yourself Every one of the fourteen stylesheets is linked from its source path; none of them comes from the merged bundle Bitrix writes under /bitrix/cache/css/ when the main module's CSS merging is on. At minimum, the five opensans.css files are 349 to 373 bytes each and hold a single @font-face rule apiece. Paste the five rules into one file you already load and five render-blocking round trips become zero.
  2. Delete the font weight nobody uses The 800 weight is declared and its stylesheet blocks rendering, but the measured run never downloaded OpenSans-ExtraBold. Nothing on the page uses it. Keep the font-display: swap that this file declares in whichever file the rules end up in.
  3. Resize the press thumbnail and re-encode the background orig.png is a 1000×1000 truecolour PNG of 717 KB placed in a card the stylesheet sizes at 10.67em tall; the seven other press thumbnails weigh 18 to 99 KB. winners.jpg is a 809 KB CSS background. Re-encoded to WebP, the same background came through our proxy at 109 KB, 86% smaller, and it is one file to replace on the server.
  4. Mark what is below the fold, and what is not None of the 18 img tags has a loading attribute and 17 of them have no width and height. loading="lazy" on the logo strip and the press slider costs one attribute per tag. Bitrix already ships a lazy-load module inside core.min.js, which this page downloads anyway; the native attribute does not even need it.
  5. Load the realtime client only for visitors who can use it The push client, its protobuf decoder and the REST client are useful to a logged-in user. The configuration this page prints for an anonymous visitor turns guest mode off. Gate the extension on authorisation in the template and a search visitor stops downloading it.

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 25 Sept 2026. Audit 462cc56d8de7a8 — 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 65 requests against the original's 104, and transferred 0.8 MB against 3 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

  • On mobile the largest element is a 41 KB product image, and in our accelerated run that exact image carried loading="lazy", the attribute that tells the browser to wait. Mobile LCP went from 9.5 s to 5.1 s, which is still above the 4 s line where Google calls it poor. A proxy should not defer the image a phone sees first, and here we did.
  • The mobile score is 75, not green. We took 16 points; the page did not become fast on a phone, it became less slow.
  • The accelerated run loaded no third-party code at all: 280 KB of tag manager, Yandex Metrica and VK scripts wait for the first interaction, and a lab test never interacts. A real visitor who scrolls or taps will download them. Blocking time on the original was already 40 ms, so the scripts cost this page bandwidth more than CPU, but part of the byte difference is analytics that did not run during the test.
  • We changed the order and the encoding of bytes, not what the site ships. The realtime client is still there through the proxy (its 50 KB protobuf file included), because from outside we cannot know which logged-in user needs it. Lighthouse still estimates 122 KB of unused JavaScript on the accelerated mobile run; that is its estimate, not a measurement.
  • The accelerated mobile run downloaded two background images (49 KB) that the original mobile run never requested, and mobile layout shift went from 0 to 0.002. Both are small, and both go the wrong way.
  • The markup quoted below is the stored copy from 3 September 2026; the scores and the resource list are the run of 25 September, which requested the same files. One page, one day: nothing here generalises to Bitrix sites as a class.