Ускорение работы сайта. Боремся с Google Page Speed

All English-language materials have been translated fully automatically using the Google service

This material is aggregate and written more for yourself. As I work and gain experience, I update it.

Use the described methods at your own risk.

Preload files and classes for php 7.4

In php 7.4, it became possible to use preloading of scripts and project files to permanently store them in memory. Let's take advantage of this in detail here

.htaccess for Bitrix

Set up compression and set caching time for resources. Detail here

Using preload to speed up

Specifying the preload specification allows the browser to set the correct priority and loading method according to the type of content being loaded. Plus, it allows you to pass one test Google Page Speed ​​

<link rel="preload" href="/js/script.js" as="script">
<link rel="preload" href="/fonts/font.woff2" as="font" type="font/woff2" crossorigin>

Load scripts and styles asynchronously

<link rel="preload" as="style" href="async.css" onload="this.rel='stylesheet'"> //асинхронно загружаем css

<script async src="async.js"></script> //асинхронно загружаем js

Lazy loading scripts

Loading the script after initializing the page

function loadScript(src) {
  let script = document.createElement('script');
  script.src = src;
  script.async = false; //Можем поставить async = true


Preloading a deferred script

<link href="async.js" rel="preload" as="script" onload="var script = document.createElement('script'); script.src = this.href; document.body.appendChild(script);">

More detail

Lazy Loading GTM

Place the GTM script in this construction

function goodbyeGTM() {
			new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],

setTimeout(goodbyeGTM, 1000);

Another option to postpone the download is to subscribe to the scroll event and call the script, only after its movement. Thanks for the method Igor Vorotnev

var fired = false;

window.addEventListener('scroll', () => {
    if (fired === false) {
        fired = true;
        setTimeout(() => {
            // Здесь все эти тормознутые трекеры, чаты и прочая ересь,
            // без которой жить не может отдел маркетинга, и которые
            // дико бесят разработчиков, когда тот же маркетинг приходит
            // с вопросом "почему сайт медленно грузится, нам гугл сказал"
        }, 1000)

Lazy Loading jivosite

(function(){ document.jivositeloaded=0;var widget_id = 'ВАШ_ВИДЖЕТ_ID';var d=document;var w=window;function l(){var s = d.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = '//'+widget_id; var ss = document.getElementsByTagName('script')[0]; ss.parentNode.insertBefore(s, ss);}//эта строка обычная для кода JivoSite
	function zy(){
		//удаляем EventListeners
		if(w.detachEvent){//поддержка IE8
		}else {
			w.removeEventListener("scroll", zy, false);
			w.removeEventListener("mousemove", zy, false);
			w.removeEventListener("touchmove", zy, false);
			w.removeEventListener("resize", zy, false);
		//запускаем функцию загрузки JivoSite
		//Устанавливаем куку по которой отличаем первый и второй хит
		var cookie_date = new Date ( );
		cookie_date.setTime ( cookie_date.getTime()+60*60*28*1000); //24 часа для Москвы
		d.cookie = "JivoSiteLoaded=1;path=/;expires=" + cookie_date.toGMTString();
	if ( ( 'JivoSiteLoaded' )<0){//проверяем, первый ли это визит на наш сайт, если да, то назначаем EventListeners на события прокрутки, изменения размера окна браузера и скроллинга на ПК и мобильных устройствах, для отложенной загрузке JivoSite.
		if(w.attachEvent){// поддержка IE8
		}else {
			w.addEventListener("scroll", zy, {capture: false, passive: true});
			w.addEventListener("mousemove", zy, {capture: false, passive: true});
			w.addEventListener("touchmove", zy, {capture: false, passive: true});
			w.addEventListener("resize", zy, {capture: false, passive: true});
	}else {zy();}

Lazy Loading

It is enough to add the async

<script type="text/javascript" async>!function(){var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src="",t.onload=function(){VK.Retargeting.Init("ВАШ_ВИДЖЕТ_ID"),VK.Retargeting.Hit()},document.head.appendChild(t)}();</script><noscript><img src="ВАШ_ВИДЖЕТ_ID" style="position:fixed; left:-999px;" alt=""/></noscript>

Lazy loading images

document.addEventListener('DOMContentLoaded', function(){
    //Блок замены src изображений для товаров в галерее
    if(document.querySelector( 'js-zoom__pic' )){
        let zoomPic = document.querySelectorAll('js-zoom__pic');

        for (let i = 0; i < zoomPic.length; i++){
                zoomPic[i].src = zoomPic[i].getAttribute('data-src');

}, false);

A separate more complex version javascript lazy-load

Lazy Loading iframe

document.addEventListener('DOMContentLoaded', function(){
    if(document.querySelector( 'js-lazy__iframe' )){
        let lazyIframe = document.querySelectorAll('js-lazy__iframe');

        for (let i = 0; i < lazyIframe.length; i++){
                lazyIframe[i].src = lazyIframe[i].getAttribute('js-lazy__iframe');
}, false);

Delayed loading Yandex maps

function loadScript(src) {
	let script = document.createElement('script');
	script.src = src;
	script.async = false; //Можем поставить async = true

function mapLoad() {
	if (ymaps) {
		let spbMap;
		let moscowMap;
		function init() {
			spbMap = new ymaps.Map('map_spb', {
				center: [59, 30],
				zoom: 15,
			}, {
				searchControlProvider: 'yandex#search',
			moscowMap = new ymaps.Map('map_moscow', {
				center: [55, 37],
				zoom: 15,
			}, {
				searchControlProvider: 'yandex#search',

			spbMap.geoObjects.add(new ymaps.Placemark([59.924812, 30.303085]));
			moscowMap.geoObjects.add(new ymaps.Placemark([55.675352, 37.470662]));
		window.addEventListener('resizeYandexMap', function (e) {

document.addEventListener("DOMContentLoaded", () => {
	setTimeout(mapLoad, 2000);

Lazy Loading youtube

Instead of iframe , we use our own div

<div class="youtube" id="8XLWGW109876" style="width:100%;height:100%;"></div>


document.addEventListener('DOMContentLoaded', function(){
    /* Блок замены youtube video */
    var videos = document.getElementsByClassName("youtube");
    var nb_videos = videos.length;

    for (var i=0; i<nb_videos; i++) {
        // Based on the YouTube ID, we can easily find the thumbnail image
        videos[i].style.backgroundImage = 'url(' + videos[i].id + '/maxresdefault.jpg)';

        // Overlay the Play icon to make it look like a video player
        var play = document.createElement("div");

        videos[i].onclick = function() {
            // Create an iFrame with autoplay set to true
            var iframe = document.createElement("iframe");
            var iframe_url = "" + + "?autoplay=1&autohide=1&enablejsapi=1";
            if (this.getAttribute("data-params")) iframe_url+='&'+this.getAttribute("data-params");

            // The height and width of the iFrame should be the same as parent

            // Replace the YouTube thumbnail with YouTube Player
            this.parentNode.replaceChild(iframe, this);
}, false);


.youtube {background-position: center;background-repeat: no-repeat;position: relative;display: block;overflow: hidden;transition: all 200ms ease-out;cursor: pointer;}
.youtube .play {background-image: url('/upload/youtube.svg');background-position: center center;background-repeat: no-repeat;background-size: 64px 64px;position: absolute;height: 100%;width: 100%;opacity: .8;filter: alpha(opacity=80);-webkit-transition: all 0.2s ease-out;-moz-transition: all 0.2s ease-out;-o-transition: all 0.2s ease-out;transition: all 0.2s ease-out;}
.youtube .play:hover {opacity: 1;filter: alpha(opacity=100);}

Optimizing images

Cutting unnecessarily large images with CFile :: ResizeImageGet . In detail here

We translate all images on the site into the webP format. Details here

Fixing Set to show all text when loading web fonts

In all font-face calls add the font-display property, like this:

@font-face {
    font-family: Roboto;
    font-style: normal;
    font-weight: 700;
    src: local('Roboto Bold'), local('Roboto-Bold'), url( format('truetype');
    font-display: swap;

A faster way to get SECTION_PAGE_URL

// этой функцией будем стоить урл
function makeUrl($parentId = 0){
    global $sections;
    $sefFolder = '/katalog/';       
    $codes = array();
    while (isset($parentId)) {
        $codes[] = $sections[$parentId]['CODE'];
            $parentId = $sections[$parentId]['IBLOCK_SECTION_ID'];
            $parentId = null;
    if (sizeof($codes)) {
        return $sefFolder . implode('/', array_reverse($codes));
    } else {
        return null;
$sections = array();
$resCatalog = CIBlockSection::GetList(
        'LEFT_MARGIN' => 'ASC'
        'IBLOCK_ID' => $arParams['IBLOCK_ID'], 
        'ACTIVE' => 'Y', 
        'GLOBAL_ACTIVE' => 'Y',
while($arCatalog = $resCatalog->Fetch()){
    $sections[$arCatalog['ID']] = $arCatalog;
foreach($sections as &$section){
    $section['SECTION_PAGE_URL'] = makeUrl($section['ID']);

Thanks for the hint Olegpro

Query Optimization

All requests should ditch GetNext () and GetNextElement () in favor of Fetch () . Using GetNext () is only justified if you need to get DETAIL_PAGE_URL or SECTION_PAGE_URL

When filtering by a property of the list type, you should abandon the PROPERTY_VALUE construction in favor of filtering by the ID property

When filtering HL blocks by exact match - you should explicitly specify = in the property

Quick retrieval of the property value of an infoblock element by property id

$el = \ElementPropertyTable::getList([
	'filter'=>['IBLOCK_ELEMENT_ID'=>$arParams['ELEMENT_ID'], 'IBLOCK_PROPERTY_ID'=>PROPERTY_ID], //where PROPERTY_ID is the id of the infoblock property

Offloading big data


$host = '';
$user = 1;

 * Начинаем с нуля или с какого то предыдущего шага
$leadID = 0;
$finish = false;

while (!$finish)
    * Выполняем пока не заберем все данные, в этом случае не стоит забывать и про задержку между хитами.
    * Либо каждый раз выбираем только 50, начиная с того элемента, на котором остановилась прошлая итерация

   $http = new \Bitrix\Main\Web\HttpClient();


   $json = $http->post(
         'order' => ['ID' => 'ASC'],
         'filter' => ['>ID' => $leadID],
         'select' => ['ID', 'TITLE', 'DATE_CREATE'],
         'start' => -1

   $result = \Bitrix\Main\Web\Json::decode($json);
   if (count($result['result']) > 0)
      foreach ($result['result'] as $lead)
         $leadID = $lead['ID'];
      $finish = true;


There are no comments yet, you can be the first to leave it

Leave a comment

The site uses a comment pre-moderation system, so your message will be published only after approval by the moderator

You are replying to a user's comment



Email me

Are you developing a new service, making improvements to the existing one and want to be better than your competitors? You have come to the right place. I offer you a comprehensive studio-level website development. From me you can order design, layout, programming, development of non-traditional functionality, implementation of communication between CMS, CRM and Data Analitics, as well as everything else related to sites, except for promotion.

Contact, I will always advise on all questions and help you find the most effective solution for your business. I am engaged in the creation of sites in Novosibirsk and in other regions of Russia, I also work with the CIS countries. You will be satisfied with our cooperation

An error occurred while sending, please try again after a while
Message sent successfully


+7(993) 007-18-96



Россия, г. Москва

By submitting the form, you automatically confirm that you have read and accept the Privacy Policy site

Contact with me
Send message
By submitting the form, you automatically confirm that you have read and accept Privacy policy of site
Sending successful!
Thank you for contacting :) I will contact you as soon as possible
Sending failed
An error occurred while sending the request. Please wait and try again after a while or call my phone number