~/www cat fonty-na-stronie-woff2-font-disp….md
Fonty na stronie a szybkość: WOFF2, font-display, preload i polskie znaki
Jak ładować fonty na stronie bez spowalniania: WOFF2, @font-face, font-display swap, preload, podzbiór z polskimi znakami i Google Fonts lokalnie.

~ xad tldr fonty-na-stronie-woff2-fon…
- Na stronę wystarczy dziś tylko WOFF2 — obsługują go wszystkie aktualne przeglądarki, a kompresja Brotli daje pliki ok. 30% mniejsze niż WOFF.
- font-display: swap pokazuje tekst od razu fontem zapasowym; optional eliminuje skok układu (CLS), ale przy wolnym łączu font może się nie pojawić.
- Preloaduj tylko jeden, najważniejszy plik WOFF2 i zawsze z atrybutem crossorigin — bez niego font pobierze się dwa razy.
- Polskie litery ą ę ł ś ż ź ć ń są w podzbiorze latin-ext, a ó w latin — przy własnym subsettingu dodaj zakresy U+0104-0107, U+0118-0119, U+0141-0144, U+015A-015B i U+0179-017C.
- Hostuj fonty u siebie: od 2020 r. przeglądarki nie współdzielą cache między domenami, a ładowanie Google Fonts z serwerów Google bez zgody sąd w Monachium uznał w 2022 r. za naruszenie RODO.
$ tree --spis-tresci
Fonty na stronie spowalniają ją wtedy, gdy są za duże, ładowane za późno albo z cudzego serwera. Żeby nie psuły wyniku w PageSpeed Insights, serwuj tylko pliki WOFF2, ustaw w @font-face właściwość font-display (zwykle swap lub optional), preloaduj jeden najważniejszy plik z atrybutem crossorigin i przytnij font do potrzebnych znaków — pamiętając, że polskie litery leżą w innym podzbiorze niż podstawowa łacinka.
Poniżej znajdziesz działające przykłady CSS, HTML i poleceń oraz odpowiedź, czy Google Fonts ładować z serwerów Google, czy lokalnie.
Jak fonty wpływają na LCP, CLS i wynik w PageSpeed
Plik fontu jest pobierany dopiero wtedy, gdy przeglądarka przetworzy HTML i CSS i stwierdzi, że widoczny tekst go używa. Do tego czasu ma dwa wyjścia:
- ukryć tekst i czekać na font (FOIT, flash of invisible text) — jeśli największym elementem na ekranie jest nagłówek, opóźnia to LCP,
- pokazać tekst fontem zapasowym i podmienić go po pobraniu (FOUT, flash of unstyled text) — tekst jest od razu czytelny, ale inne wymiary liter przesuwają układ, co podbija CLS.
Optymalizacja sprowadza się więc do trzech rzeczy: mniej bajtów, wcześniejsze pobranie i kontrolowana podmiana fontu. Progi Core Web Vitals i sposób ich mierzenia opisuje poradnik jak przetestować stronę internetową.
Formaty fontów: dlaczego dziś wystarczy WOFF2
| Format | Co to jest | Na stronie |
|---|---|---|
| TTF (TrueType) | klasyczny plik fontu systemowego | nie — bez kompresji, duży |
| OTF (OpenType) | plik systemowy, często z krzywymi CFF | nie — jak wyżej |
| WOFF | „opakowanie” TTF/OTF z kompresją zlib | zbędny, chyba że musisz wspierać bardzo stare przeglądarki |
| WOFF2 | opakowanie z kompresją Brotli i przekształceniem tabel fontu | tak, jedyny potrzebny |
| EOT, SVG | dawne formaty dla Internet Explorera i starego iOS | nie |
WOFF2 jest rekomendacją W3C od 2018 roku i obsługują go wszystkie aktualne przeglądarki. Według web.dev kompresuje około 30% lepiej niż WOFF. Długie listy src z formatami eot, ttf i svg w starych motywach to relikt. TTF lub OTF zostaw jako plik źródłowy do subsettingu i konwersji.
@font-face krok po kroku: przykład z WOFF2
Minimalna, poprawna deklaracja dla fontu w dwóch grubościach wygląda tak:
@font-face {
font-family: "Lato";
src: url("/fonts/lato-400.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Lato";
src: url("/fonts/lato-700.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
body {
font-family: "Lato", Arial, sans-serif;
}
Kilka zasad, które oszczędzają kłopotów:
- Ta sama nazwa w
font-familydla wszystkich grubości i stylów — przeglądarka sama dobierze plik pofont-weightifont-style. Nazwy w stylu"Lato Bold"psują dziedziczenie i powodują sztuczne pogrubianie. - Ładuj tylko te grubości, których naprawdę używasz. Każda to osobny plik.
- Deklaracje
@font-faceumieść w CSS ładowanym w<head>(najlepiej w krytycznym CSS osadzonym w HTML), a nie w pliku dociąganym przez@import. - Serwer powinien wysyłać pliki z nagłówkiem
Content-Type: font/woff2i długimCache-Control(np.max-age=31536000, immutable) przy wersjonowanej nazwie pliku. Fonty dobrze buforuje też sieć CDN.
Pełny opis deskryptorów znajdziesz w dokumentacji MDN o font-display i @font-face.
font-display: swap, optional czy fallback?
Właściwość font-display określa, jak długo przeglądarka ukrywa tekst (okres blokowania) i jak długo jeszcze może podmienić font zapasowy na docelowy (okres zamiany). Orientacyjne czasy podaje poradnik web.dev o dobrych praktykach dla fontów:
| Wartość | Blokowanie | Zamiana | Efekt |
|---|---|---|---|
block | ok. 2–3 s | bez limitu | niewidoczny tekst, gorszy LCP; tylko dla fontów ikon |
swap | ok. 0 ms | bez limitu | tekst od razu, później podmiana i możliwy skok układu |
fallback | ok. 100 ms | ok. 3 s | kompromis: spóźniony font nie zostanie już podmieniony |
optional | ok. 100 ms | brak | brak skoku układu; przy wolnym łączu zostaje font zapasowy |
auto | zależy od przeglądarki | zależy od przeglądarki | w praktyce zwykle zbliżone do block |
Jak wybrać:
swap— domyślny wybór dla tekstu, zwłaszcza gdy font jest częścią identyfikacji marki. Żeby nie płacić za niego wyższym CLS, dopasuj font zapasowy (o tym niżej).optional— najlepszy dla CLS. Na wolnym łączu pierwsza wizyta pokaże font systemowy, kolejna — docelowy z cache. Dobrze łączy się z preloadem.fallback— rozsądny środek, gdy nie chcesz późnej podmiany po kilku sekundach czytania.
W adresie Google Fonts odpowiada za to parametr &display=swap, który generator Google dodaje domyślnie.
Preload fontu: tylko jednego pliku i zawsze z crossorigin
Preload pozwala zacząć pobieranie fontu od razu, zamiast czekać na CSS:
<link rel="preload" href="/fonts/lato-400.woff2" as="font" type="font/woff2" crossorigin>
Atrybut crossorigin jest wymagany nawet dla fontu z Twojej własnej domeny. Fonty są zawsze pobierane w trybie CORS, więc preload bez tego atrybutu nie zostanie dopasowany do żądania z @font-face i plik pobierze się dwa razy (Chrome pokaże wtedy ostrzeżenie w konsoli).
Zasady:
- Preloaduj tylko font widoczny od razu na górze strony, i to jeden, góra dwa pliki — każdy konkuruje o łącze z obrazem LCP.
- Preload ignoruje
unicode-range, więc pobierze wskazany plik nawet wtedy, gdy strona go nie użyje. - Ścieżka w
hrefmusi być identyczna jak w@font-face, łącznie z parametrami wersji.
Jeżeli zostajesz przy Google Fonts z serwerów Google, preload pliku nie ma sensu (adresy plików się zmieniają). Zamiast tego dodaj preconnect:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
Polskie znaki w fontach na stronie: subsetting i unicode-range
Subsetting to wycięcie z fontu glifów, których nie używasz: cyrylicy, greki, znaków wietnamskich czy symboli. Trzeba przy tym uważać, żeby nie wyciąć polskich liter.
Jak dzieli fonty Google Fonts: latin i latin-ext
Google Fonts dzieli kroje na podzbiory z własnym unicode-range, a przeglądarka pobiera tylko te, których znaki występują na stronie. Dla łacinki są dwa:
latin— zakresU+0000-00FF(ASCII i Latin-1) plus typograficzne znaki zU+2000-206F, m.in. polskie cudzysłowy „ ” i półpauza. Tu jest ó i Ó (U+00F3,U+00D3).latin-ext— m.in. zakresU+0100-02BA, w którym leżą ą ę ł ś ż ź ć ń i ich wielkie odpowiedniki.
Efekt: polska strona pobiera z Google Fonts dwa pliki na każdą grubość, bo prawie każdy akapit zawiera ą lub ł. A kto przy samodzielnym hostingu skopiował tylko plik latin, dostaje ó w docelowym kroju, a ą i ł — w foncie zapasowym. Jak szybko sprawdzić, czy dany krój w ogóle ma polskie litery, opisuje poradnik jak sprawdzić, czy czcionka ma polskie znaki.
Własny podzbiór „latin + polskie znaki”
Dla polskiej strony najlepiej zrobić jeden plik na grubość, zawierający Latin-1, polskie litery i interpunkcję. Polskie znaki spoza Latin-1 to dokładnie te zakresy:
| Litery | Zakres Unicode |
|---|---|
| Ą ą Ć ć | U+0104-0107 |
| Ę ę | U+0118-0119 |
| Ł ł Ń ń | U+0141-0144 |
| Ś ś | U+015A-015B |
| Ź ź Ż ż | U+0179-017C |
| Ó ó | U+00D3, U+00F3 (w Latin-1) |
Podzbiór zrobisz narzędziem pyftsubset z pakietu fontTools. Do zapisu WOFF2 potrzebny jest też moduł brotli:
pip install fonttools brotli
pyftsubset Lato-Regular.ttf \
--unicodes="U+0000-00FF,U+0104-0107,U+0118-0119,U+0141-0144,U+015A-015B,U+0179-017C,U+2000-206F,U+20AC,U+2122" \
--layout-features='*' \
--flavor=woff2 \
--output-file=lato-400-pl.woff2
Opcja --layout-features='*' zachowuje wszystkie funkcje OpenType (np. cyfry tabelaryczne, alternatywne glify). Bez niej fontTools zostawia zestaw domyślny z kerningiem i podstawowymi ligaturami, a plik jest mniejszy.
Dla skali: zmienny Inter z repozytorium Google Fonts (osie opsz i wght) waży jako TTF ok. 857 KB, po samej konwersji do WOFF2 ok. 331 KB, a po przycięciu powyższymi zakresami z domyślnym zestawem funkcji OpenType — ok. 65 KB. Wszystkie polskie litery, cudzysłowy „” i półpauza zostają.
W CSS warto dopisać pasujący unicode-range, żeby przeglądarka wiedziała, które znaki ten plik obsługuje:
@font-face {
font-family: "Lato";
src: url("/fonts/lato-400-pl.woff2") format("woff2");
font-weight: 400;
font-display: swap;
unicode-range: U+0000-00FF, U+0104-0107, U+0118-0119, U+0141-0144,
U+015A-015B, U+0179-017C, U+2000-206F, U+20AC, U+2122;
}
Uwaga: Przed subsettingiem sprawdź licencję fontu. Kroje z Google Fonts są w większości na licencji SIL Open Font License, która pozwala na modyfikację i samodzielny hosting. Fonty komercyjne często mają w licencji osobne zasady dla wersji webowej.
glyphhanger: podzbiór na podstawie treści strony
glyphhanger (autorstwa Zacha Leathermana) to narzędzie w Node.js, które odwiedza stronę, zbiera użyte znaki i wywołuje pyftsubset za Ciebie. Wymaga zainstalowanych wcześniej fontTools i brotli:
npm install -g glyphhanger
# jakie znaki występują na stronie (i do 10 podstron)
glyphhanger https://twojadomena.pl --spider
# podzbiór Latin z dopisanymi polskimi literami
glyphhanger --LATIN --whitelist="ąćęłńóśźżĄĆĘŁŃÓŚŹŻ„”–" --formats=woff2 --subset="Lato-*.ttf"
Przycinanie dokładnie do znaków z dzisiejszej treści jest ryzykowne: nowy artykuł z literą spoza listy wyświetli ją fontem zapasowym. Na blogu czy w sklepie bezpieczniej trzymać się zakresu „Latin-1 + polskie znaki”.
Konwersja TTF do WOFF2
Jeśli nie potrzebujesz podzbioru, wystarczy sama konwersja:
pyftsubsetz--unicodes="*"i--flavor=woff2— konwersja bez wycinania znaków,woff2_compressz oficjalnego repozytorium Googlegoogle/woff2(w Debianie i Ubuntu pakietwoff2):
sudo apt install woff2
woff2_compress Lato-Regular.ttf # tworzy Lato-Regular.woff2 obok
- bez instalowania czegokolwiek: konwerter TTF do WOFF2 działający lokalnie w przeglądarce (ang.) — pliki nie są nigdzie wysyłane, a całą rodzinę przekonwertujesz naraz.
Fonty zmienne (variable fonts): jeden plik zamiast kilku grubości
Font zmienny zawiera w jednym pliku całą oś grubości (a czasem też szerokości, pochylenia czy rozmiaru optycznego). Deklarujesz go zakresem:
@font-face {
font-family: "Inter";
src: url("/fonts/inter-var-pl.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
h1 { font-weight: 750; }
Opłaca się przy trzech lub więcej grubościach — jeden plik zmienny jest zwykle mniejszy niż suma statycznych. Przy jednej–dwóch grubościach statyczne bywają lżejsze. Google Fonts serwuje kroje zmienne automatycznie: w arkuszu dla Inter z grubościami 400 i 700 obie reguły @font-face wskazują ten sam plik WOFF2.
Subsetting działa także na fontach zmiennych — pyftsubset zachowuje osie.
Google Fonts lokalnie czy z serwerów Google?
W 2026 roku praktycznie zawsze lepiej hostować fonty u siebie. Powody są trzy.
Prywatność i RODO: wyrok z Monachium
20 stycznia 2022 r. Sąd Krajowy w Monachium (LG München I, sygn. 3 O 17493/20) uznał, że osadzenie Google Fonts ładowanych z serwerów Google bez zgody użytkownika narusza RODO, bo przeglądarka przekazuje Google adres IP odwiedzającego. Sąd zasądził od właściciela strony 100 euro zadośćuczynienia i wskazał, że nie ma tu uzasadnionego interesu, skoro fonty można hostować lokalnie. To wyrok niemieckiego sądu pierwszej instancji, niewiążący w Polsce, ale po nim w Niemczech posypały się masowe wezwania do zapłaty, a lokalny hosting stał się standardem.
Wydajność: koniec współdzielonego cache
Dawny argument „użytkownik ma już Roboto z Google w cache” nie działa od lat. Chrome od wersji 86 (2020), Firefox od 85, a Safari jeszcze wcześniej dzielą cache HTTP według odwiedzanej witryny — ten sam plik z fonts.gstatic.com pobrany na innej stronie jest dla Twojej strony pobierany od nowa.
Z serwerami Google dochodzą dwa dodatkowe połączenia (fonts.googleapis.com i fonts.gstatic.com) i blokujący renderowanie arkusz CSS. U siebie fonty idą tym samym połączeniem HTTP/2 co reszta strony i możesz je preloadować. Web.dev zaznacza jednak, że self-hosting nie jest szybszy automatycznie — na wolnym hostingu bez HTTP/2 może przegrać z Google.
Jak przenieść Google Fonts na swój serwer
- Pobierz arkusz CSS z nowoczesnym User-Agentem — Google zwraca wtedy pliki WOFF2 z gotowymi
unicode-range:
UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0 Safari/537.36"
curl -A "$UA" "https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap" -o lato.css
grep -o "https://fonts.gstatic.com[^)]*" lato.css # adresy plików WOFF2
- Pobierz pliki z podzbiorów
latinilatin-ext(inne, np.cyrillic, możesz pominąć), skopiuj ich bloki@font-facedo swojego CSS i zamień adresy na lokalne. - Albo zrób własny podzbiór „latin + PL” z pliku TTF, jak wyżej — wychodzi jeden plik na grubość zamiast dwóch.
- Usuń z szablonu
<link>do fonts.googleapis.com i ewentualne@import.
Jeśli wolisz gotowy zestaw bez wiersza poleceń, generator webfontów z gotowym CSS @font-face (ang.) przerobi pliki TTF lub OTF w przeglądarce na komplet WOFF2 z arkuszem stylów (domyślnie z font-display: swap) i stroną podglądu. Polskie znaki sprawdź potem na podglądzie — narzędzie konwertuje cały font, więc liczy się to, co ma plik źródłowy.
Font zapasowy z size-adjust i ascent-override: mniej CLS
Przy swap skok układu bierze się z różnicy wymiarów fontu zapasowego i docelowego: zmienia się łamanie wierszy i wysokość linii. Zniwelujesz to, tworząc dopasowany font zapasowy z lokalnego Ariala:
@font-face {
font-family: "Inter Fallback";
src: local("Arial");
size-adjust: 109%;
ascent-override: 89%;
descent-override: 22%;
line-gap-override: 0%;
}
body {
font-family: "Inter", "Inter Fallback", sans-serif;
}
Skąd te liczby (przybliżone, dla pary Inter → Arial):
size-adjustto stosunek średniej szerokości znaków obu fontów — tu ok. 0,527 em dla Intera do ok. 0,482 em dla Ariala,ascent-overrideidescent-overrideto metryki fontu docelowego podzielone przezsize-adjust(Inter: wydłużenia górne 1984 i dolne 494 jednostek przy 2048 jednostkach na em).
Dokładniejsze wartości policzą biblioteki ważące częstość liter (np. Capsize, Fontaine), a Next.js robi to automatycznie. Wynik sprawdź wzrokowo, przełączając w DevTools font docelowy na zapasowy.
Ważne: Według danych caniuse Safari (stan na 2026 r.) obsługuje
size-adjust, ale ignorujeascent-overrideidescent-override. Na iPhone’ach dopasowanie będzie więc tylko częściowe.
WordPress: motywy i wtyczki, które ładują Google Fonts
W WordPressie fonty dodają motyw, kreator stron i wtyczki — często każde osobno, więc strona potrafi ładować ten sam krój dwa razy.
- Otwórz stronę z DevTools (F12) → Network → filtr Font i odśwież. Sprawdź, czy są żądania do fonts.gstatic.com i ile plików się pobiera.
- W motywach blokowych (WordPress 6.5+) użyj wbudowanej Biblioteki fontów w edytorze witryny (Wygląd → Edytor → Style → Typografia). Zainstalowany z niej krój Google WordPress pobiera na Twój serwer, domyślnie do katalogu
wp-content/uploads/fonts. - W motywach klasycznych i kreatorach (Elementor, Divi i podobne) poszukaj w ustawieniach opcji lokalnego ładowania fontów lub wyłączenia Google Fonts — wiele popularnych motywów i kreatorów ją ma. Wybierając motyw, sprawdź to przed instalacją; zestawienie motywów WordPress pomoże zawęzić wybór.
- Gdy motyw nie daje takiej opcji, wtyczka OMGF (w katalogu wordpress.org) przechwytuje żądania do Google Fonts i serwuje kopie z Twojego serwera.
Ostrożnie z wtyczkami „optymalizacyjnymi”, które przepisują CSS. Gdy dwie naraz lokalizują fonty, łatwo o zgubiony podzbiór latin-ext — i polskie litery w innym kroju. Po każdej zmianie obejrzyj tekst z ą, ę i ł.
Jak zmierzyć efekt: PageSpeed Insights, DevTools i WebPageTest
Zmierz stan przed zmianami i po nich, w tych samych warunkach.
- PageSpeed Insights — w Lighthouse 13 dawny audyt „Ensure text remains visible during webfont load” zastąpiła analiza Font display w sekcji Insights. Dane z terenu (CrUX) zmienią się dopiero po kilku tygodniach.
- Chrome DevTools, zakładka Network, filtr Font — liczba plików, ich rozmiar, kolumna Initiator (kto zażądał fontu) i czy preloadowany plik nie pobiera się drugi raz.
- Elements → Computed → sekcja Rendered Fonts na dole: zaznacz akapit z polskimi literami i sprawdź, czy wszystkie glify pochodzą z „Network resource”. Jeśli część jest z „Local file” (np. Arial), Twój podzbiór nie ma tych znaków.
- Performance → nagranie ładowania strony → ścieżka Layout shifts pokaże, czy podmiana fontu przesuwa układ.
- WebPageTest — wykres waterfall pokaże, kiedy fonty zaczynają się pobierać względem CSS, a widok filmstrip — moment podmiany tekstu. Szczegóły w poradniku jak czytać wykres waterfall w WebPageTest.
Lista kontrolna: fonty na stronie
- Tylko WOFF2, najwyżej 2–4 pliki na stronę (przy wielu grubościach font zmienny).
- Podzbiór: Latin-1, polskie litery i
U+2000-206F. font-display: swapalbooptional, gdy liczy się przede wszystkim CLS.- Jeden preload z
as="font",type="font/woff2"icrossorigin. - Fonty na własnym serwerze z długim
Cache-Control, bez żądań do fonts.googleapis.com. - Font zapasowy dopasowany przez
size-adjustiascent-override. - Kontrola w DevTools: Rendered Fonts na tekście z ą ę ł ś ż ź ć ń ó.
~ man faq
Najczęściej zadawane pytania
Jaki format fontu wybrać na stronę internetową?
WOFF2. Obsługują go wszystkie współczesne przeglądarki, a pliki są wyraźnie mniejsze niż TTF, OTF i WOFF. TTF lub OTF zostaw do instalacji w systemie i jako plik źródłowy do konwersji.
Co daje font-display: swap?
Przeglądarka od razu wyświetla tekst fontem zapasowym, a po pobraniu fontu webowego podmienia go. Tekst nie jest niewidoczny, ale podmiana może przesunąć układ strony, co zwiększa CLS.
Jak hostować Google Fonts lokalnie?
Pobierz pliki WOFF2 (np. z arkusza CSS Google Fonts albo repozytorium projektu), wrzuć je na swój serwer i zapisz własne reguły @font-face z font-display i unicode-range. Usuń z kodu strony linki do fonts.googleapis.com i sprawdź w DevTools, że nie ma już żądań do domen Google.
Dlaczego na stronie polskie znaki wyświetlają się inną czcionką?
Font lub jego podzbiór nie zawiera polskich liter, więc przeglądarka bierze je z fontu zapasowego. Najczęściej brakuje podzbioru latin-ext albo własny subset został zrobiony tylko z zakresu Latin-1.
Czy preload fontu przyspiesza stronę?
Tak, jeśli dotyczy fontu używanego w treści widocznej od razu, bo przeglądarka zaczyna pobieranie przed przetworzeniem CSS. Preload kilku plików naraz konkuruje jednak o łącze z obrazem LCP i CSS, więc ogranicz się do jednego lub dwóch.
Ten artykuł jest częścią tematu
$ whoami
Założyciel i redaktor XAD.pl. Pisze o sieciach, bezpieczeństwie IT, administracji systemami Windows i Linux oraz o sprzęcie, który sprawia ludziom problemy na co dzień.


