SEO Tag Auto-Generation Setup for Bitrix CMS
SEO tag auto-generation — filling <title>, <meta name="description"> and <h1> according to rules based on element or section data. Needed when catalog contains thousands of items: manual filling isn't practical, and empty meta tags harm indexing.
Standard Templates in SEO Module
Basic tool — meta tag rules in Marketing → Search Engine Optimization → Meta Tags. For infoblock, set template with substitutions #NAME#, #SECTION_NAME#, #PROPERTY_{CODE}#. Covers most cases without programming.
For online store, typical template set:
| Object | Title | Description |
|---|---|---|
| Catalog Element | #NAME# — price, specifications |
Buy #NAME#. #PROPERTY_SHORT_DESC# Delivery from 1 day. |
| Catalog Section | #NAME# — product catalog |
Wide selection in #NAME# section. #PROPERTY_SECTION_DESC# |
| Article Detail | #NAME# | Blog |
#PREVIEW_TEXT# |
Custom Generation via Events
When templates aren't enough (complex logic needed: different templates by property, adding related element data), use OnPageStart or OnBeforeProlog event handler in init.php:
AddEventHandler('main', 'OnBeforeProlog', function() {
global $APPLICATION;
// Only for catalog detail pages
if (!preg_match('#^/catalog/([^/]+)/([^/]+)/#', $_SERVER['REQUEST_URI'])) {
return;
}
// Can't get current element from component directly,
// so use URL data or request separately
$elementId = (int)$_REQUEST['ELEMENT_ID'];
if (!$elementId) return;
$el = \CIBlockElement::GetByID($elementId)->GetNext();
if (!$el) return;
$brand = $el['PROPERTIES']['BRAND']['VALUE'] ?? '';
$model = $el['NAME'];
$price = \CCatalogProduct::GetByID($elementId)['PRICE'] ?? '';
$APPLICATION->SetPageProperty('title',
"{$brand} {$model} — buy for {$price} $ | Online Store"
);
$APPLICATION->SetPageProperty('description',
"Buy {$brand} {$model} for {$price} $ with delivery across country. " .
($el['PROPERTIES']['SHORT_DESC']['VALUE'] ?? '')
);
});
SetPageProperty call in OnBeforeProlog executes before page components, so the value reaches <head>.
Generating H1 Independently from Title
<title> and <h1> should differ — good practice. <h1> set via page property h1:
$APPLICATION->SetPageProperty('h1', $el['NAME']);
In page template:
<h1><?= $APPLICATION->GetPageProperty('h1') ?: $APPLICATION->GetTitle() ?></h1>
Mass Update of SEO Fields for Existing Elements
If need to fill SEO fields for thousands of existing products by template — done via script:
$res = \CIBlockElement::GetList(
[],
['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'ACTIVE' => 'Y'],
false,
false,
['ID', 'NAME', 'IBLOCK_ID']
);
while ($el = $res->GetNextElement()) {
$fields = $el->GetFields();
$props = $el->GetProperties();
$seoTitle = $fields['NAME'] . ' — buy in online store';
$seoDesc = 'Buy ' . $fields['NAME'] . ' with delivery. ' .
($props['SHORT_DESC']['VALUE'] ?? '');
\CIBlockElement::SetPropertyValuesEx($fields['ID'], $fields['IBLOCK_ID'], [
'SEO_TITLE' => $seoTitle,
'SEO_DESCRIPTION' => $seoDesc,
]);
}
Script runs once. After execution, elements get filled SEO fields that override module template.
Implementation Timeline
Setting up auto-generation templates via SEO module — 1–2 hours. Custom generation with event logic + mass update of existing elements — 4–6 hours.







