Critical CSS Optimization for 1C-Bitrix
A typical picture: a Bitrix site loads three or four CSS files in <head> with a combined size of 300–600 KB. The browser does not begin rendering the page until it has downloaded and parsed all CSS — this is called render-blocking. As a result, FCP (First Contentful Paint) is 3–5 s on a mobile connection even with caching enabled. The solution is to extract the critical CSS (above-the-fold styles) into an inline <style> block and defer the loading of the rest.
Where Render-blocking CSS Comes From in Bitrix
Bitrix builds the CSS file list via CMain::AddCSS() and outputs it in $APPLICATION->ShowHead(). All files appear in <head> as <link rel="stylesheet"> — blocking loading by default. The main module can bundle CSS via its built-in combiner (/bitrix/cache/css/), but it does not separate critical from non-critical CSS.
Additionally: components like bitrix:catalog.section add their own styles via $APPLICATION->SetAdditionalCSS(), and component templates add styles via style.css inside the component folder. All of this ends up in the blocking path.
What Critical CSS Means in Practice
Critical CSS is the set of styles needed to render content above the fold without additional network requests. For a Bitrix site this typically includes:
- Reset/normalize (
box-sizing, base margins) - Header and first page block grid
- H1, H2 heading and body text typography
- Navigation menu styles
- Hero banner or first slider styles
Everything else — product card styles, cart, filter form, footer — can be loaded asynchronously.
The Critical CSS Inlining Technique
Step 1: Extract Critical Styles
Use critical (Node.js) for automated extraction:
npm install -g critical
critical https://example.com --width=1300 --height=900 \
--css=public/bitrix/templates/main/template_styles.css \
--inline \
--output=critical.css
For mobile viewport, add a second run with --width=375 --height=812. Merge the results — you get a critical.css of 15–40 KB.
Step 2: Integration into the Bitrix Template
In the template's header.php:
// Inline critical CSS
$criticalCss = file_get_contents(__DIR__ . '/critical.css');
?>
<style><?= $criticalCss ?></style>
<?php
// Async loading of remaining CSS
?>
<link rel="preload" href="/local/templates/main/template_styles.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<noscript>
<link rel="stylesheet" href="/local/templates/main/template_styles.css">
</noscript>
rel="preload" as="style" with switching to stylesheet via onload is the standard LoadCSS pattern. <noscript> is a fallback for browsers without JavaScript.
Step 3: Working with the Bitrix CSS Combiner
If the built-in combiner is enabled (BX_COMPOSITE_BUFFER_ON_CSS), it intercepts <link> output. Either disable it for controlled files:
// In header.php before ShowHead()
define('BX_COMPOSITE_BUFFER_ON_CSS', false);
Or use the OnEndBufferContent event for post-processing HTML and replacing blocking <link> tags with asynchronous ones.
Working with PostCSS and PurgeCSS
On projects where the template is built via Gulp or Webpack, add to the pipeline:
// gulpfile.js
const purgecss = require('@fullhuman/postcss-purgecss');
postcss([
purgecss({
content: ['./local/templates/**/*.php', './local/components/**/*.php'],
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
})
])
PurgeCSS analyzes PHP templates and removes unused rules. On Bootstrap/Foundation-based themes this reduces CSS from 250 KB to 30–60 KB.
Case Study: Corporate Portal on Bitrix24
A company portal with ~300 employees: the home page loaded 7 CSS files with a combined size of 480 KB. FCP in Lighthouse (Desktop, Fast 3G) — 4.2 s. The goal: reduce to 1.5 s without changing the template.
What was done:
- Ran
criticalfor the home page, news section, and documents section — three different critical CSS sets. - Added logic in
header.phpto detect the page type and include the corresponding inline CSS. - Migrated remaining CSS files to
preload+ async loading. - PurgeCSS removed ~60% of unused rules from
template_styles.css.
Result: FCP — 1.3 s, total above-the-fold CSS weight — 18 KB inline, the remaining 90 KB loading asynchronously after FCP.
Automating Critical CSS Updates
Critical CSS must be updated when the design changes. Embed in the deploy script:
#!/bin/bash
# post-deploy.sh
node ./scripts/generate-critical.js
php artisan cache:clear # or bitrix/modules/main/tools/critical_css.php
The generate-critical.js script launches headless Chrome via Puppeteer, captures critical CSS from a list of key pages, and writes files to the template folder.
Diagnostics
In Chrome DevTools → Coverage (Shift+Ctrl+P → Coverage) you can see the percentage of unused CSS. A figure above 70–80% unused CSS on the first screen is a direct signal to act. In Lighthouse — the "Reduce unused CSS" metric shows potential savings in KB.
Timeline
| Scale | Scope | Duration |
|---|---|---|
| Basic | Manual critical CSS extraction + inline in header.php | 2–3 days |
| Medium | Automation via critical + async link loading + deploy-time update |
4–6 days |
| Full | PurgeCSS in the build, multiple critical CSS sets per page type, CI integration | 7–10 days |







