Font Optimization for 1C-Bitrix
Fonts are one of the most underestimated sources of page load delay. A typical Bitrix project has this situation: the site loads 4–6 Google Fonts weights via @import directly in CSS, each @import blocks rendering, the browser waits for the CSSOM, and the user sees a blank page or the "Flash of Invisible Text" (FOIT). Lighthouse flags this as "Ensure text remains visible during webfont load" and directly harms the LCP metric.
How Bitrix Loads Fonts by Default
In most themes, fonts are loaded in one of three ways:
-
@import url('https://fonts.googleapis.com/...')insidetemplate_styles.css— the slowest option: the browser discovers the request only after downloading CSS. - A
<link>in the template'sheader.phpwithoutpreconnectandpreloadattributes. - Direct loading of
.woff/.woff2files via CDN without caching headers.
Bitrix does not manage fonts through the core — everything lives in the template, so changes must be made there, not through the main module settings.
Key Optimization Techniques
Self-hosted Fonts Instead of Google Fonts
Loading fonts from an external CDN adds a DNS lookup + TCP handshake + TLS negotiation — totaling 100 to 300 ms on a cold connection. Move fonts to your own server:
# Download via google-webfonts-helper or npx
npx google-webfonts-helper --family="Roboto" --subsets="latin,cyrillic" --variants="400,700"
Place files in /local/templates/[template]/fonts/, CSS in fonts.css:
@font-face {
font-family: 'Roboto';
src: url('/local/templates/main/fonts/roboto-400.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
unicode-range: U+0400-045F, U+0490-0491; /* Cyrillic only */
}
font-display: swap is mandatory. Without it, the browser blocks text rendering until the font is loaded.
Preload for Critical Weights
Add preload in the template's header.php for the weight used on the first screen:
// header.php
$APPLICATION->AddHeadString(
'<link rel="preload" href="/local/templates/main/fonts/roboto-400.woff2" as="font" type="font/woff2" crossorigin="anonymous">'
);
Preload works for one or two weights. Preloading all variants is counterproductive: the browser downloads them with high priority, competing with HTML and critical CSS.
Subsetting: Removing Unnecessary Glyphs
The full Roboto font weighs 150–200 KB per weight. For a site in English or any single-script language, only the latin subset is needed. For multilingual sites include only the required Unicode ranges. Using pyftsubset (fonttools):
pyftsubset roboto-regular.ttf \
--unicodes="U+0020-007E,U+0400-045F,U+0490-0491,U+00A0" \
--flavor=woff2 \
--output-file=roboto-400-subset.woff2
Result — a file of 20–35 KB instead of the original 150+ KB.
Variable Fonts
If the design uses multiple weights of the same typeface (Regular, Medium, Bold), consider a variable font — one file replaces several:
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-VF.woff2') format('woff2 supports variations'),
url('/fonts/Roboto-VF.woff2') format('woff2');
font-weight: 100 900;
font-display: swap;
}
The Roboto variable font weighs ~75 KB and covers all weights — versus 4×30 KB = 120 KB for individual subset files.
Case Study: Building Materials Online Store
A store on Bitrix "Business" loaded 3 font families via Google Fonts: Roboto (3 weights), Open Sans (2), Oswald (1). That's 6 HTTP requests to fonts.googleapis.com + 6 requests to fonts.gstatic.com. The render-blocking delay was 480–620 ms, LCP on mobile — 4.8 s.
After migrating to self-hosted with subsetting and a single preload for Roboto 400:
- LCP dropped to 2.1 s
- Total font weight: from 820 KB to 94 KB
- Render-blocking from fonts: 0 ms (thanks to
font-display: swap+ preload)
The work took 2 days: analyzing the current setup, preparing subset files, editing header.php and fonts.css, testing in Lighthouse and WebPageTest.
Diagnosing Issues
Quick check via DevTools: Network → Font tab. If the Initiator column says stylesheet (not preload), the font is loading reactively, not proactively. Achieving an FCP in Lighthouse below 1.8 s with render-blocking fonts present is practically impossible without the optimizations described above.
Timeline
| Scale | Scope | Duration |
|---|---|---|
| Basic | Self-hosted + font-display: swap + preload |
1–2 days |
| Full | Subsetting, variable fonts, audit of all templates, caching headers setup | 3–5 days |







