Imagine: your product page shows '42 people viewing right now' while only three real visitors are on the site. Bots and an unoptimized counter distort the data, erode trust, and overload the server. We have tuned this mechanism dozens of times — from small catalogs to projects with a million visitors per month. The approach must be fast, plausible, and light on the server. Using Redis instead of MySQL can reduce server resource costs by up to 40%.
Typical Problems
- Stale data: updating once a minute makes numbers irrelevant.
- Database load: every page request hits the DB.
- Bot distortion: without proper filtering, the counter shows 100+ 'people' when only a few are real.
- Optimal activity window: 5–10 minutes, minimum display threshold: 2 viewers.
What 'Viewing Right Now' Means
In practice, we count users who opened a product page in the last 2–15 minutes (the store chooses the window). Typical choice: 5–10 minutes.
Two main storage strategies: Option 1 — MySQL/PostgreSQL table. Create bl_product_viewers:
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 view, insert or update the record by (product_id, session_id). Count: SELECT COUNT(DISTINCT session_id) WHERE product_id = ? AND last_seen > NOW() - INTERVAL 5 MINUTE. A cleanup agent runs every 10 minutes.
Option 2 — Redis. Key viewers:product:{id} is a sorted set with score = timestamp, member = session_id. Count: ZCOUNT viewers:product:123 (NOW-300) +inf. Remove stale entries with ZREMRANGEBYSCORE. Faster and avoids DB load, but requires Redis on the server.
Why Redis?
Redis delivers sub-millisecond reads/writes — about 10 times faster than MySQL for counter operations. It offloads MySQL and reduces server costs by up to 40%. AJAX responses become snappy.
Bitrix Implementation
In component_epilog.php of the product detail page, record each visit:
$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 . "'"); Move the count query to a separate AJAX controller — never run SELECT in the epilog. Learn more about component_epilog on the official Bitrix documentation.
AJAX Counter Update
On the client, refresh without reload:
setInterval(() => { fetch('/ajax/product-viewers/?id=' + productId) .then(r => r.json()) .then(data => { if (data.count > 1) { document.querySelector('.viewers-count').textContent = 'Currently viewing: ' + data.count; } }); }, 30000); Register the AJAX handler in Bitrix via CModule::AddAutoloadClasses() or as a standalone PHP file including the kernel.
Bot Exclusion
Filter by User-Agent in component_epilog.php before recording. Use a regex matching bot, crawler, spider, etc. This is mandatory — without it, numbers are inflated by 30–40%. For built-in Bitrix mechanisms, you can use BITRIX_SESSID and IsUserAuthorized, but that does not block search engine bots.
Step-by-Step Setup
- Choose storage: MySQL for small projects, Redis for high-load.
- Create data structure: table or sorted set.
- Record visits: in
component_epilog.phpwith bot filtering. - AJAX controller: returns JSON with active viewer count.
- Client JS: updates counter every 30 seconds.
- Cleanup agent: SQL query for MySQL,
EXPIREor background cleanup for Redis. - Set thresholds and declensions: for polished output.
Storage Comparison
| Parameter | MySQL/PostgreSQL | Redis |
|---|---|---|
| Write speed | ~5 ms | <1 ms |
| Read speed | ~3 ms (with index) | <0.5 ms |
| Server load | Medium (disk I/O) | Low (RAM) |
| Requirements | Existing DB | Redis installation |
| Scalability | Horizontal | Vertical |
Work Stages
| Stage | Description | Time |
|---|---|---|
| Analysis | Choose activity window, estimate load | 2 hours |
| Design | Select storage, plan schema | 1 hour |
| Implementation | Record visits, AJAX controller, JS | 4–6 hours |
| Testing | Bot checks, load testing | 2 hours |
| Deployment | Agent setup, caching | 1 hour |
What's Included
- Creating
bl_product_viewerstable (or Redis key structure) - Recording visits in
component_epilog.phpwith bot filtering - AJAX controller returning live viewer count
- JavaScript component for periodic updates
- Cleanup agent for stale records
- Output block with proper conditions (threshold, declension)
Contact us — we will implement this turnkey in 1–3 days. With over 5 years of experience and 50+ successful custom counter projects, our certified Bitrix specialists ensure reliable deployment. This real-time visitor counter acts as a powerful social proof widget, boosting conversion optimization. With guaranteed bot filtering and proven Redis caching, you save up to 40% on server resources compared to a MySQL-only approach — that is $200–$400 per month for medium-traffic stores. For high-traffic stores, savings can exceed $500 monthly. Our team uses advanced Bitrix Framework features like CModule::AddAutoloadClasses, agent functions, and event handlers to ensure seamless integration. Additionally, we leverage caching engines and composite site techniques to further reduce load.
This implementation covers the Bitrix product view counter with AJAX counter update, bot filtering, and Redis for counters to build a reliable real-time visitor counter that serves as a social proof widget and boosts conversion optimization while saving server resources.

