I removed my Chinese web font. Every page lost 1 MB

Every Chinese page on this site downloaded about 1 MB of font before a reader saw a word. I replaced it with the font already on the reader's device. Here are the numbers before and after, the CSS, what I gave up, and what it did not fix.

Also available in 中文

What a Chinese page was carrying

Before the change, every Chinese page on this site carried 94,098 bytes of font instructions inside its own HTML, before a single font file was requested. That is 100 @font-face rules, the same 100 on every page.

This site is bilingual. Every page exists in English and in Simplified Chinese, so the English page is a fair control for its Chinese twin. Same layout, same pictures, same words in another language.

The English version of the mamak table post was 51 KB of HTML. The Chinese version was 143 KB. After the HTML arrived, the Chinese page went on to download 20 font files, 1,107 KB in total. The Chinese home page downloaded 16 files, 857 KB.

On a phone, Lighthouse scored that Chinese post 55 and 65 on two runs. The English one had scored 97 two days earlier.

A reader in Kuala Lumpur on mobile data was paying for a megabyte of font to read one blog post.

Why a Chinese font is so heavy

A Latin font needs a few hundred shapes. A Chinese font needs many thousands, and each one is a drawing.

Google Fonts handles this well for what it is. It cuts a Chinese family into about a hundred slices and tells the browser which characters live in which slice. That is what the 100 rules were. The browser then downloads only the slices that contain the characters on the page.

The catch is that ordinary Chinese prose is spread across many slices. One post of mine needed 20 of them. The slicing saved me from a font of several megabytes and still left me with one megabyte.

It also has a cost that is easy to miss: the 100 rules themselves. They were inlined in every Chinese page so the font could start loading early, which made every Chinese page nearly three times the size of its English twin before any font arrived.

The change: four lines of CSS

The fix was to stop sending a Chinese font and name the ones readers already have. Every iPhone, iPad and Mac ships with PingFang SC. Android ships with Noto Sans CJK, the same design I had been downloading. Windows ships with Microsoft YaHei.

html[lang='zh-Hans'] {
  --font-zh: 'PingFang SC', 'Hiragino Sans GB', 'Noto Sans CJK SC', 'Noto Sans SC', 'Source Han Sans SC', 'Microsoft YaHei', sans-serif;
  --font-sans: 'Geist Variable', var(--font-zh);
}

The third line puts the Latin font first, so English words and numbers inside Chinese text are still drawn in this site's own typeface, Geist. The browser falls through to the Chinese list only for Chinese characters.

That is the whole change in the stylesheet. The rest was deleting things: the font entry in the build configuration, the tag that loaded it, and the 100 rules that came with it.

I work with two Claude sessions, a Brain that plans and a Hand that builds. The Brain proposed this. I did not approve it on the idea. I approved it after the Hand had measured it and put screenshots of both versions side by side.

Before and after

After the change, a Chinese page requests exactly the same two font files as an English page. These are the numbers for the Chinese mamak table post, measured with Lighthouse mobile on the same machine, the same day, two runs each.

Before After
Font files 20 2
Font weight 1,107 KB 54 KB
HTML 143 KB 51 KB
HTML, compressed 48 KB 15.6 KB
Whole page 1,464 KB 318 KB
Largest Contentful Paint 3.7 s and 4.4 s 2.9 s and 2.2 s
Lighthouse mobile score 68 and 73 84 and 97

The Chinese home page went from 1,796 KB to 901 KB, and from scores of 40 and 42 to 51 and 55. It is still low, and I know why: the home page runs a 3D scene, which is a separate cost I wrote about. The font was half of that page's weight. It was not the whole problem.

What I gave up

What I gave up is control: the same page now looks slightly different on an iPhone, an Android phone and a Windows laptop. With a web font, every reader saw the same shapes.

One thing had to be checked before I agreed: that bold is really bold. A system font with only one weight makes the browser fake the bold by smearing the regular shape, and it shows. On a Mac, the browser drew the real faces: PingFang SC Regular for body text, Medium for headings, Semibold for bold.

I have not seen this site on Windows or on an Android phone with my own eyes, as of this writing. Microsoft YaHei has fewer weights than PingFang, so headings there may look lighter than I designed. I accepted that. A page that arrives is worth more than a page that matches.

What it did not fix

The first live measurement after shipping scored 76, not 97. The largest element took 5.6 seconds to appear. I had saved a megabyte and the phone score barely showed it.

I went looking for a cause and found a real one. The same day I had changed how analytics loads, and two Google scripts, 297 KB together, were now starting with every page and competing with the content. I moved them to load after the page is idle or at the reader's first touch. Three live runs on the same Chinese post after that: 92, 97 and 99.

But the scripts do not explain the 76. Before that fix went out, the same page on the same code was measured twice more and scored 95 and 92. The 76 was mostly one bad run. Moving the scripts lifted the typical score by a few points, not by twenty.

Two lessons from that. One measurement is not a result: the same page on the same code scored 76 and 95 a few hours apart, and I nearly built a story on the 76. And a page has more than one weight on it. Removing the biggest one mostly reveals the next.

One small thing is still open. In Chinese, a line should not start with a particle like 的. CSS has a property meant to break lines by phrase. I tried it. In the browsers I tested, it did nothing for Chinese. I left it.

How to check your own site

You can find out what your Chinese pages download in about two minutes, with no paid tool.

  1. Open a Chinese page of your site in Chrome on a computer.
  2. Open DevTools, go to Network, choose the Font filter, and reload.
  3. Count the files and read the total at the bottom. If you see twenty files or a megabyte, you have what I had.
  4. View the page source and search for @font-face. If the count is in the dozens, your HTML is carrying the font instructions too.
  5. Open the same page in your other language and compare the two totals. The difference is what your Chinese readers pay.

If the number is large, try the stack above on one page, look at it on your own phone, and decide with your eyes.

FAQ

Should I use a web font or system fonts for a Chinese website?

For body text, use system fonts. A Chinese web font costs roughly a megabyte per page even when it is sliced and loaded well, and every modern phone and computer already has a good Chinese font installed. Keep a web font only for a short headline or a logo where the exact shape is the brand, and only after measuring what it costs.

Why is Noto Sans SC so slow to load?

Because Chinese needs thousands of characters and each one is a drawing. Google Fonts splits Noto Sans SC into about a hundred slices so a page downloads only what it uses, but ordinary prose still touches many slices. One ordinary post on this site needed 20 font files, 1,107 KB.

What is a good CSS font stack for Simplified Chinese?

This site uses "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Noto Sans SC", "Source Han Sans SC", "Microsoft YaHei", sans-serif, with the Latin font placed before it. It covers Apple devices, Android and Windows in that order, and ends in the browser's own default.

Do system fonts make a Chinese website look worse?

They make it look slightly different on each device, not worse. PingFang SC, Noto Sans CJK and Microsoft YaHei are the fonts your readers see in every app on their own phone. Check that bold text uses a real bold face, and look at the page on a real device before you decide.