SEO & Marketing

Optimizing Persian Fonts on Your Website; Fixing Text Shift and Reducing Size

A practical guide to eliminating text shift when loading Persian fonts. From font-display and subsetting to actually measuring FOIT and FOUT with free tools.

SEO & Marketing

Your Website's Text Is Invisible for a Few Milliseconds

You open the page. The content is there, but the letters aren't. Two seconds later, everything suddenly appears, and the page layout shifts. This is exactly the moment an Iranian user experiences when working with your site, and the reason is your Persian font.

The problem starts because Persian fonts are heavy. A complete Vazirmatn file with 4 regular weights is between 400 and 700 kilobytes. The Variable version of Vazirmatn even reaches 1 megabyte. Compare this size with a woff2 file, which after compression usually becomes 20 to 30 percent of the original size. But even this compressed size, when loaded on Iran's mobile network with an 80 to 150 millisecond delay, means several hundred milliseconds of delay in displaying text.

During this interval, the browser has two choices. Either it shows the text with the system's default font (FOUT) or it keeps it completely hidden (FOIT). Both states are disastrous for user experience. The first causes layout shift, or CLS, and the second confronts the user with an empty page.

The solution isn't removing the font. The solution is precisely controlling this moment.

font-display: The First Decision You Need to Make

Consider the following CSS output:

@font-face {
  font-family: 'Vazirmatn';
  src: url('/fonts/vazirmatn.woff2') format('woff2');
  font-display: swap;
  font-weight: 400;
}

The swap value tells the browser: "Show the text immediately with the default font; when the font is ready, replace it." This means the user never sees empty text. But its cost is layout shift. Because a Persian font usually has a different line height and letter width compared to the system's default font.

The block value works exactly the opposite. The browser waits up to 3 seconds, and if the font doesn't arrive, it shows the text with a fallback font. This maximizes FOIT and is usually the wrong choice for a Persian site whose font is on a domestic server.

Also take the optional value seriously. This value tells the browser that if the font doesn't arrive very quickly, don't use it at all and keep the system font forever. The result? A user who doesn't see the font but also experiences no shift. For sites where the font is part of the brand identity, this means a complete design failure.

Here's where they make mistakes: many think swap solves all problems. But look at their results in Google PageSpeed Insights. CLS goes from 0.15 to 0.38, and the report says "use font-display: optional." Don't blindly follow this recommendation. If the Persian font is critical for your text's readability, keep swap and solve the CLS problem with infrastructure techniques, not by sacrificing the font.

Actually Measuring the Problem

Before any changes, record the current state. In Chrome DevTools, open the Performance tab and load the page once with Network Throttling set to Slow 4G. Note two numbers: FCP and CLS. Then, in the Network tab, find the font file and look at the Waterfall column. If the font starts downloading after 1.5 seconds, the problem is elsewhere: your font is referenced lower in the HTML than necessary or is blocked by another CSS.

Another practical tip: check the font file with the following command:

curl -sI https://example.com/fonts/vazirmatn.woff2 | grep -E "HTTP|content-length|cache-control"

If cache-control doesn't include a max-age of at least 31536000, the user's browser will download the font again on every visit. This means every time the user navigates to the next page of your site, they wait for the font again.

Persian Font Subsetting; Where Size Really Decreases

A complete Persian font includes 600+ glyphs: letters, numbers, symbols, and ligatures. But how many of them does your page need at any given moment? The answer: fewer than 100. Subsetting means dividing the font file into several parts and sending only the part the browser actually needs.

The free tool pyftsubset from the fonttools project does this. Installation and execution:

pip install fonttools brotli
pyftsubset vazirmatn.ttf \
  --unicodes="U+0600-06FF,U+0750-077F,U+FB50-FDFF,U+FE70-FEFF" \
  --flavor=woff2 \
  --output-file=vazirmatn-fa.woff2

This command keeps only Arabic and Persian glyphs and removes the rest. The result for Vazirmatn usually goes from 400 kilobytes to 120-150 kilobytes. Now define this file in CSS with unicode-range:

@font-face {
  font-family: 'Vazirmatn';
  src: url('/fonts/vazirmatn-fa.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+FB50-FDFF, U+FE70-FEFF;
  font-display: swap;
}

Modern browsers only download this file when they find text with this Unicode range on the page. For a page that only has Persian, this means immediate download. For an English page, it means no download at all.

Go one step further: subset the font based on frequently used characters. The fonttools subset tool allows this with the --text flag:

pyftsubset vazirmatn.ttf \
  --text="ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیئ،.؛:!؟۱۲۳۴۵۶۷۸۹۰" \
  --flavor=woff2 \
  --output-file=vazirmatn-body.woff2

This file is sufficient for your site's body text, and its size reaches 30-40 kilobytes. But be careful: if a user types text with a character outside this list, or if you have a page with special words, those letters won't be displayed. For this reason, use this method only for the body font and keep the complete font for headings and special elements.

Combining Subsetting with Preload

Subsetting reduces size but not network latency. To ensure the font downloads from the very beginning of page load, announce it to the browser with preload:

<link rel="preload" href="/en/fonts/vazirmatn-fa.woff2" as="font" type="font/woff2" crossorigin>

Place this line in the <head>. The browser immediately starts downloading the font without waiting for the CSS to arrive. The practical result: the font is usually ready 200-400 milliseconds earlier.

Here's where they make mistakes: they apply preload to the complete font file. The result is that 400 kilobytes are downloaded from the start, even if the page only needs 50 kilobytes of font. Always put preload on the smallest subset needed for initial rendering.

Accept FOUT, but Manage It

With font-display: swap, the user first sees text with the system font and then with the main font. If this shift isn't controlled, it raises CLS and ruins the reading experience. You have two ways to manage this shift.

The first way: choose a fallback font whose dimensions are close to the main font. For Vazirmatn, the suggested system font on Linux is usually DejaVu Sans, whose line height is about 15% greater. You can compensate for this difference by adjusting line-height in CSS:

body {
  font-family: 'Vazirmatn', 'DejaVu Sans', sans-serif;
  line-height: 1.8;
}
.fonts-loaded body {
  line-height: 1.7;
}

The second way: with JavaScript, add the fonts-loaded class to the <body> after the font finishes loading. This is simple with document.fonts.ready:

document.fonts.ready.then(function () {
  document.body.classList.add('fonts-loaded');
});

This method gives you complete control. You can activate the font earlier for specific elements like headings and wait for body text. The final result is a shift the user doesn't notice, not one that moves the entire page.

Keep the Persian Font on a Domestic Server

If your font is on an external CDN or Google Fonts server, every font request must cross the border. The latency of this path in the worst case reaches 300 milliseconds. Put the font on the same server where your site runs and keep its path same-origin. This has another advantage too: with HTTP/2 on a domestic server, the font, CSS, and JavaScript load over a single TCP connection, eliminating the cost of establishing a new connection.

To check where your font is actually being served from, use the domain and DNS technical checker tool and see which IP the font address resolves to. If the IP is outside Iran, it means every time the font is downloaded, the user waits for it to cross the border.

Optimization Order; Where to Start

If your site currently has a text shift problem, follow this order:

  1. Subset the font and keep only Persian glyphs. Reduce the size from 400 to under 150 kilobytes.
  2. Enable font-display: swap and put preload on the subsetted file.
  3. Host the font on a domestic server and set the cache-control: max-age=31536000 header.
  4. Manage layout shift with document.fonts.ready.
  5. Finally, measure with PageSpeed Insights and Chrome DevTools to ensure CLS is below 0.1 and FCP is under 1.8 seconds.

If you've done these steps and CLS is still high, the problem isn't the font. Images or other page elements are probably rendering without specified dimensions. Read the guide on fixing CLS layout shift on Persian websites and address those.

One final note: don't keep the font on your server forever. Check for new font versions every few months. Persian fonts like Vazirmatn are regularly updated, and new versions usually have better glyphs and sometimes smaller sizes. Updating the font is a simple CSS change that's worth it.

If you're looking to improve your site's overall speed, read the guide on increasing website speed from click to render. And if these optimizations are time-consuming for you, the SEO services team can do this work for you.

Frequently Asked Questions

Why is my Persian font loading slowly?

The main reasons are threefold: the large size of the font file, lack of subsetting, and hosting the font on an external server. A complete Persian font is usually over 400 kilobytes, and if it's on an external CDN, it must cross the border every time. With subsetting and domestic hosting, loading time usually decreases to one-third.

Is font-display: swap better or optional?

If the Persian font is critical to your site's brand identity and readability, choose swap and manage layout shift with other techniques. optional is only suitable when the font is a decorative enhancement and its absence doesn't harm user experience. On Persian news and e-commerce sites, swap is usually the right choice.

How much size does Persian font subsetting reduce?

A complete Persian font with 600 glyphs is usually 400-700 kilobytes. By keeping only Arabic and Persian glyphs, the size reaches 120-150 kilobytes. If you keep only frequently used characters, the size decreases to 30-40 kilobytes. This reduction is typically 70 to 90 percent.

How do I know if my site's font is causing layout shift?

In Chrome DevTools, open the Performance tab and load the page with throttling set to Slow 4G. If you see a Layout Shift bar in the recording that activates simultaneously with font loading, the font is the culprit. You can also temporarily disable the font and measure CLS again. If CLS drops below 0.05, the font is the main factor.

ServerNet Support

ServerNet engineering & editorial team — specialists in infrastructure, networking and web hosting.

SEO Services
Share:

Comments 0

No comments yet — be the first!

Leave a comment

Related service

SEO Services

SEO is not a cost, it's an investment. With technical SEO, content strategy and principled link building, ServerNet raises your ranking on Google and builds real, lasting organic traffic.