Fixing Silent Glyph/Font Failures in MapLibre Styles (a debugging story)
Some bugs announce themselves with a stack trace. Others just quietly remove a feature and let you find out from a user. This is one of the second kind — and it’s common enough in self-hosted MapLibre setups that it deserves its own writeup, because the failure mode is genuinely misleading.
The symptom
Everything works. The map loads, tiles render, roads and boundaries show up exactly where they should. You test it in the browser, on desktop Chrome, and it looks complete. You ship it.
Then a label disappears. Not an error — just empty space where a street name or a town name should be. Zoom in, zoom out, still nothing. No console error. No failed network request that stands out. The map looks fine, it’s just missing text.
If you only tested on desktop, you might not catch this at all — the failure is often more visible on mobile devices than in browser dev tools, which is exactly the wrong order to find out about it.
Why this happens
MapLibre doesn’t render label text as plain text — it renders it using pre-generated glyph files (PBF-encoded font atlases, one per font weight, covering ranges of Unicode code points). Your style.json points to a glyph source URL, and each layer that shows text references a specific font name.
Two ways this quietly breaks:
1. A demo or third-party glyph URL. Starter styles and tutorials often ship with a glyph URL pointing at a vendor’s demo server (a common one is a MapTiler demo key baked into example configs). That works fine while you’re testing — the demo server responds — until it doesn’t: rate limits, key expiration, or the vendor deciding demo keys shouldn’t work in production. When it fails, MapLibre doesn’t throw a rendering error. It just has no glyphs for that font, so it silently draws no text.
2. A font name mismatch. Your self-hosted glyph files are generated for specific font family names — and they’re often not what you’d guess. A generic name like “Noto Sans Regular” might not match what your specific glyph set was actually built as (for example, a Klokantech-packaged variant would need “Klokantech Noto Sans Regular”, not the generic name). Reference the wrong string in your style.json‘s text-font property, and MapLibre looks for glyphs under a name that doesn’t exist in your glyph directory — again, no error, just missing labels.
Both failures produce the exact same symptom: perfect-looking map, missing text. That’s what makes this bug expensive to debug if you don’t know to check glyphs first — you’ll suspect your data, your tile generation, your style layers, anything but the one component (fonts) that doesn’t announce when it’s broken.
How to actually diagnose it
Skip guessing and check these in order:
- Open your glyph URL directly in a browser. If your style references
https://your-domain.com/fonts/{fontstack}/{range}.pbf, manually substitute a real font name and range (e.g.Klokantech Noto Sans Regular/0-255.pbf) and request it. A 404 or an unexpected response confirms the glyph source itself is the problem. - List your actual glyph directory. Whatever font names exist as folders on disk are the only valid values for
text-fontin your style — not what you assume the font is called, not what a tutorial told you, what’s actually on disk.
ls /opt/tiles/fonts/
- Grep your style.json for every
text-fontreference and confirm each one matches a folder from step 2 exactly, character for character.
grep -r "text-font" style.json
- Test on an actual mobile device, not just desktop Chrome — some rendering paths and caching behavior differ enough that a glyph issue can be less visible on desktop.
The fix
Self-host your glyphs from the start rather than relying on a demo endpoint — this removes failure mode #1 entirely. Generate them with a tool like fontnik or font-maker from your chosen font files, and serve the resulting PBF directory through the same Nginx layer as your tiles.
For failure mode #2, the fix is just discipline: after generating glyphs, immediately note the exact folder name it produced, and use that string — copy-pasted, not retyped from memory — everywhere your style references text-font.
{
"layout": {
"text-font": ["Klokantech Noto Sans Regular"],
"text-field": ["get", "name"]
}
}
Why this bug is worth writing about
It’s not a hard fix. It’s maybe a five-line change once you know what’s wrong. What makes it worth a full writeup is the shape of the failure: nothing crashes, nothing logs an error, the map looks 95% correct, and the missing 5% is exactly the kind of detail (street names, place labels) that a casual glance won’t catch — but that a real user navigating your app absolutely will.
The general lesson carries past this specific bug: in self-hosted map infrastructure, “renders without errors” and “renders correctly” are not the same claim. Test the specific feature, on the specific device, not just the absence of a red error in the console.
