Static Call Tracking on 1C-Bitrix Without Losing Cache

Imagine: you launch ad campaigns in Yandex.Direct and Google Ads, calls come in—but you don't know which channel each customer came from. Without call attribution, you waste budget on inefficient sources. Static call tracking on 1C-Bitrix solves this, but hits a caching wall. If you output a dynamic

Our competencies:

Frequently Asked Questions

Imagine: you launch ad campaigns in Yandex.Direct and Google Ads, calls come in—but you don't know which channel each customer came from. Without call attribution, you waste budget on inefficient sources. Static call tracking on 1C-Bitrix solves this, but hits a caching wall. If you output a dynamic number in a template, Bitrix stops caching the page—performance drops.

We have been configuring static call tracking on Bitrix projects for over five years. Let's dive into how the scheme works, what pitfalls hide in caching, and why JS substitution is the optimal solution. This approach is cheaper than dynamic: you need fewer numbers. For example, when tracking five channels, monthly number rental is 30–50% cheaper than a dynamic scheme, which requires one number per unique visitor. But attribution only goes down to the channel level, not the keyword. For B2B, medical, real estate—it's justified.

Static Call Tracking Mechanism

You take N numbers from a provider—one per tracked channel. Each number forwards to the main line. The call tracking system records which static number was called and logs it.

Channel Number Forwarding
Yandex.Direct +7 (495) 111-11-11 → main
Google Ads +7 (495) 222-22-22 → main
VKontakte +7 (495) 333-33-33 → main
Offline / business cards +7 (495) 444-44-44 → main
Website (default) +7 (495) 555-55-55 → main

Implementation on Bitrix

We store the channel-to-number mapping in the module settings. We determine the channel via utm_source from GET parameter or cookie.

// /local/modules/local.calltracking/lib/Config.php namespace Local\Calltracking; class Config { public static function getPhoneMap(): array { return [ 'yandex' => [ 'display' => '+7 (495) 111-11-11', 'href' => 'tel:+74951111111', ], 'google' => [ 'display' => '+7 (495) 222-22-22', 'href' => 'tel:+74952222222', ], 'vk' => [ 'display' => '+7 (495) 333-33-33', 'href' => 'tel:+74953333333', ], 'offline' => [ 'display' => '+7 (495) 444-44-44', 'href' => 'tel:+74954444444', ], 'default' => [ 'display' => '+7 (495) 555-55-55', 'href' => 'tel:+74955555555', ], ]; } } 

Hook into OnProlog—capture utm_source and write a cookie:

// /local/php_interface/init.php \Bitrix\Main\EventManager::getInstance()->addEventHandler( 'main', 'OnProlog', function () { $request = \Bitrix\Main\Application::getInstance()->getContext()->getRequest(); // Priority 1: GET parameter utm_source (new visit) $utmSource = $request->get('utm_source'); if ($utmSource) { $utmSource = preg_replace('/[^a-z0-9_-]/i', '', strtolower($utmSource)); setcookie('ct_source', $utmSource, time() + 86400 * 30, '/'); $_COOKIE['ct_source'] = $utmSource; } // Priority 2: stored cookie if (!$utmSource) { $utmSource = $_COOKIE['ct_source'] ?? 'default'; } $GLOBALS['CT_CURRENT_SOURCE'] = $utmSource; } ); 

Helper for inserting the number into the template:

namespace Local\Calltracking; class PhoneResolver { public static function getCurrentPhone(): array { $source = $GLOBALS['CT_CURRENT_SOURCE'] ?? 'default'; $map = Config::getPhoneMap(); return $map[$source] ?? $map['default']; } } 

Usage in the header:

<?php $phone = \Local\Calltracking\PhoneResolver::getCurrentPhone(); ?> <a href="<?= htmlspecialchars($phone['href']) ?>" class="header-phone" data-source="<?= htmlspecialchars($GLOBALS['CT_CURRENT_SOURCE'] ?? 'default') ?>"> <?= htmlspecialchars($phone['display']) ?> </a> 

Why Does Bitrix Cache Conflict with Call Tracking?

Static call tracking via PHP logic is incompatible with full caching. The phone number is personalized data (depends on cookie), so the page with the phone should not be cached. As noted in the caching documentation, personalized data require disabling component caching, but JS substitution bypasses this limitation.

Three solution options:

Option Description Cache Complexity
JS substitution Default number in HTML, replaced by client script Preserved Low
Exclude from cache Disable cache for block via $component->arResult Partial Medium
ESI Fragment via Edge Side Includes Preserved High

Why JS Substitution Is the Optimal Choice?

Optimal is JS substitution. Here's the script:

document.addEventListener('DOMContentLoaded', function () { const phoneMap = { 'yandex': {display: '+7 (495) 111-11-11', href: 'tel:+74951111111'}, 'google': {display: '+7 (495) 222-22-22', href: 'tel:+74952222222'}, // ... 'default': {display: '+7 (495) 555-55-55', href: 'tel:+74955555555'}, }; const source = getCookie('ct_source') || 'default'; const phone = phoneMap[source] || phoneMap['default']; const el = document.querySelector('.header-phone'); if (el) { el.textContent = phone.display; el.href = phone.href; } }); 

We use this approach in most projects. The replacement happens in milliseconds, the user doesn't notice any flickering. Preserving full page cache gives a loading speed increase of up to 300% compared to disabling cache. Additionally, JS substitution requires no extra server resources—all logic runs client-side.

For example, we recently implemented static call tracking for a service industry client with five advertising channels. After setup, attribution showed that 40% of calls came from Yandex.Direct, 30% from Google Ads, 20% from social networks, and the rest from offline advertising. The client was able to reallocate budget and increase overall conversion by 25% by dropping inefficient channels. Savings on number rental exceeded 60% compared to dynamic call tracking.

What Are the Limitations of Static Call Tracking?

Static call tracking cannot track the keyword, only the channel. For detailed attribution by search query, you need dynamic call tracking with on-the-fly number substitution. However, dynamic requires more numbers and is harder to integrate with cache. The static approach is justified for businesses where attribution at the channel level is sufficient.

How to Integrate a Call Tracking Provider?

A provider (Callibri, CoMagic, Mango) is needed for call recording and statistics. Setup:

  1. Rent the required number of numbers.
  2. For each number, set a rule: "if call comes to number X, consider source Y."
  3. Forward to the main number.
  4. Optionally—record conversations.

No Bitrix API is needed—all logic is on the provider side.

What Is Included in the Work

  • Renting substitute numbers and configuring forwarding.
  • Developing a PHP module for channel detection and cookie storage.
  • Implementing JS phone substitution (cache-compatible variant).
  • Adapting templates (header, footer, forms).
  • Setting up channel mapping in the admin panel.
  • Documentation for support and training your administrator.

Timeline: full setup for 4–6 channels—3–7 working days. The cost is determined after a project audit.

For quick start, contact us—we'll prepare a proposal within one day. We have over 50 implementations for agencies and direct advertisers. We guarantee cache preservation and correct call attribution. Get a consultation—we'll assess your project.