Skip to content
Development

The favicon that was broken for months and nobody would have known

By Victor Da Luz
astroperformancedev-logsite

A performance-review spike had flagged four small things on imperfectsystems.com: the homepage was rendering server-side for no reason, two logo PNGs were way bigger than they needed to be, and the favicon “likely” didn’t work. That last one stuck with me. Not “doesn’t work” - “likely doesn’t work.” Nobody had actually looked.

The easy half

The homepage was the simplest fix in the whole batch: one line, export const prerender = true;. Every page component it renders pulls from build-time constants, nothing dynamic anywhere in the tree. Before, every visit invoked the Worker to render identical HTML. After, it’s a static file. Building confirmed it: dist/client/index.html showed up in the prerendered routes list for the first time, and as a bonus, two byte-identical stylesheets that used to ship separately for the homepage and the blog merged into one. Didn’t touch that on purpose. It just stopped being a problem.

The logos were almost as simple, once I found out I didn’t have the tool I expected. The plan called for WebP, but this machine has no WebP encoder at all - no cwebp, no ImageMagick, and sips refuses to write the format. What it does have is pngquant and oxipng, so I stayed in PNG. The hero logo went from 772K to 159K with a palette-quality pass tuned high enough that I couldn’t see banding in the gradient glow (I tried a more aggressive setting first - it shaved off another 20K but you could just start to see it, so I backed off). The header logo was the bigger win: it’s rendered at 32 pixels but the source file was 291. Resize to 96 for retina, compress, done - 98K down to under 5K.

The favicon was where “likely” turned into “actually”

The SVG was three lines: a black background rect, and an <image> tag pointing at the existing logo PNG as an external file. Completely reasonable-looking code. The problem is that browsers load favicon SVGs in a locked-down rendering mode - the same restriction that applies to a plain <img src="icon.svg"> - and external resource references get silently dropped in that mode. Not an error, not a console warning. The image tag just doesn’t paint anything, and you’re left looking at whatever the background shape was.

The fix is to stop referencing an external file and embed the image data directly in the SVG as a base64 data: URI. Once it’s inline, there’s no external reference to block. I generated the URI from the newly-optimized (already-tiny) logo and wrote it into the SVG - a self-contained file instead of one with a silent runtime failure baked into it.

Here’s the part I almost skipped: I could have called that done, since the syntax is standard and well-documented, and moved on trusting it. But “likely renders as a bare rect” was exactly the kind of claim that got this whole thing flagged in the first place, and I didn’t want to replace one unverified guess with another - even a well-informed one. qlmanage, the same engine behind macOS Quick Look, can rasterize an SVG straight to a PNG thumbnail from the command line. Ran it against the fixed file: chip logo, clean, correct colors. That’s the difference between “this should work now” and “I looked at it and it works.”

What stuck with me

The favicon had probably been broken since the file was created. Nothing crashes when a favicon fails silently - the tab just shows a generic icon or nothing, and there’s no error to trip over. It’s the kind of bug that survives indefinitely specifically because failure looks like nothing at all. The fix took ten minutes. Noticing it needed fixing took someone writing “verify, don’t assume” into the check phase and then actually doing that, instead of trusting a plausible-looking three-line SVG - or, just as easily, trusting a plausible-looking fix without ever rendering it.

Related reading