Setting up the "N people are viewing this product" notification in 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages

Configuring the "N People Are Viewing This Product" Notification for 1C-Bitrix

A notification showing the number of simultaneous viewers creates a sense of scarcity and urgency. "12 people are looking at this right now" nudges the purchase decision. The key word here is "right now": the data must reflect real-time activity, not accumulated statistics.

How to Define "Viewing Right Now"

The concept of "viewing right now" is approximate. In practice, users who opened the product page within the last 2–15 minutes are counted (the store chooses the window itself). A typical choice is 5–10 minutes.

Two strategies are used to store active views:

Option 1 — MySQL/PostgreSQL table. A bl_product_viewers table is created:

CREATE TABLE bl_product_viewers (
    id          INT AUTO_INCREMENT PRIMARY KEY,
    product_id  INT NOT NULL,
    session_id  VARCHAR(64) NOT NULL,
    last_seen   DATETIME NOT NULL,
    INDEX idx_product_time (product_id, last_seen)
);

On each page view, a record is inserted or updated by (product_id, session_id). Count query: SELECT COUNT(DISTINCT session_id) WHERE product_id = ? AND last_seen > NOW() - INTERVAL 5 MINUTE. An agent cleans stale records every 10 minutes.

Option 2 — Redis. The key viewers:product:{id} is a sorted set where score = timestamp of the last visit, member = session_id. Count: ZCOUNT viewers:product:123 (NOW-300) +inf. Stale entries are removed via ZREMRANGEBYSCORE. This is faster and does not stress the database, but requires Redis on the server.

Bitrix-Side Implementation

In component_epilog.php of the product detail page, the visit is recorded:

$productId  = $arResult["ID"];
$sessionId  = session_id();
$now        = date("Y-m-d H:i:s");

$DB->Query("INSERT INTO bl_product_viewers (product_id, session_id, last_seen)
    VALUES (" . intval($productId) . ", '" . $DB->ForSql($sessionId) . "', '" . $now . "')
    ON DUPLICATE KEY UPDATE last_seen = '" . $now . "'");

Counting active views is offloaded to a separate AJAX controller — running a SELECT in the epilog would slow down every page request.

AJAX Counter Updates

The counter updates on the client without a page reload. A JavaScript component makes a request every 30–60 seconds:

setInterval(() => {
    fetch('/ajax/product-viewers/?id=' + productId)
        .then(r => r.json())
        .then(data => {
            if (data.count > 1) {
                document.querySelector('.viewers-count').textContent =
                    'Viewing now: ' + data.count;
            }
        });
}, 30000);

The AJAX handler in Bitrix is registered via CModule::AddAutoloadClasses() or as a standalone PHP file with the core loaded via require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php'.

Protection Against Unrealistic Numbers

Minimum display threshold — 2 or 3 people. Showing "1 person is viewing this product" is pointless and looks implausible. Maximum threshold — if the number seems unrealistically high (e.g., 500 people), a soft cap or randomization within an acceptable range can be introduced.

Bots are excluded from the count using the same approach as in the view counter: checking User-Agent in component_epilog.php before writing to the table.

What Is Included in the Setup

  • Creating the bl_product_viewers table (or configuring Redis keys)
  • Recording visits in component_epilog.php with bot filtering
  • AJAX controller for retrieving the current viewer count
  • JavaScript component with periodic counter updates
  • Agent for cleaning up stale records
  • Displaying the block with correct conditions (threshold, pluralization)