flexweg-favicon
flexweg-favicon turns one uploaded square image into the full favicon + PWA manifest cluster every modern browser expects, then injects the matching <link> tags into every published page.
It's must-use because every public site benefits from favicons — the cost of having it always-on is one <link> cluster per page, and the benefit is "your site looks like a real site in browser tabs and bookmark bars."
What it generates
From a single uploaded source image (PNG / JPG / WebP / SVG):
| File | Path on Flexweg | Format |
|---|---|---|
favicon-96x96.png | /favicon/favicon-96x96.png | 96×96 PNG (browser tab) |
apple-touch-icon.png | /favicon/apple-touch-icon.png | 180×180 PNG with white background (iOS home screen) |
web-app-manifest-192x192.png | /favicon/web-app-manifest-192x192.png | 192×192 PNG, purpose: maskable |
web-app-manifest-512x512.png | /favicon/web-app-manifest-512x512.png | 512×512 PNG, purpose: maskable |
favicon.ico | /favicon/favicon.ico | Multi-size (16/32/48) packed via pure-JS encoder |
favicon.svg | /favicon/favicon.svg | Passthrough only when source is SVG |
site.webmanifest | /favicon/site.webmanifest | PWA manifest (name, short_name, icons, theme_color, …) |
All resizing happens client-side via createImageBitmap + canvas cover-crop. No server, no third-party API.
Settings
/settings/plugin/flexweg-favicon exposes:
Source tab
- Upload image — square image, ideally 512×512 or larger. Drag-and-drop or click to pick.
- Preview — shows the cropped result for each variant.
- Generate + Upload — runs the full pipeline. ~5-10 s for a typical source.
PWA tab
- App name — used as
namein the manifest. Falls back to the site title. - Short name —
short_name; falls back to truncated app name. - Theme colour — browser chrome tint when the PWA is installed. Also injected as
<meta name="theme-color">. - Background colour — splash screen background.
- Display mode —
standalone(default),fullscreen,minimal-ui,browser. - Re-upload manifest — pushes only
site.webmanifest(no source image required). Useful for tweaking PWA settings without re-running the resize pipeline.
How it hooks in
api.addFilter<string>("page.head.extra", (current, props) => {
const config = readConfig(props);
const tags = buildHeadTags(config);
return tags ? `${current}\n${tags}` : current;
});
The filter emits only the tags whose corresponding files were actually uploaded — so a partial install (e.g. raster source so no SVG variant) doesn't produce broken <link> references. Each URL is cache-busted via ?v=<uploadedAt> so changing the favicon invalidates browser caches on the next page load.
The plugin also registers a Regeneration target for Themes → Regenerate site → Favicon manifest that re-uploads site.webmanifest only. (The PNG/ICO/SVG variants would require the source image, which the plugin doesn't store — re-uploading them means going back to the Source tab and re-clicking Generate.)
Per-format flags
The plugin tracks which variants were successfully uploaded:
{
hasIco: true,
hasSvg: false, // source was raster
hasPng96: true,
hasAppleTouch: true,
hasManifest: true,
uploadedAt: 1735689600000,
}
Each flag gates one <link> tag in the output. So toggling SVG support doesn't require code changes — it follows from whether the source was an SVG.
Toggle
Disabling the plugin (in code, since it's MU and has no UI toggle) would stop emitting the <link> tags. Files at /favicon/ would persist on Flexweg until manually deleted.
When changes apply
- Newly published pages include the new tags immediately (with the new
?v=query string). - Already-published pages keep their old
<link>tags with the old?v=. Browsers may serve cached favicons until the page is regenerated.
To roll out a favicon change site-wide: Themes → Regenerate site → All HTML pages.
Internal details
- Source:
src/mu-plugins/flexweg-favicon/ - Hooks used:
page.head.extrafilter;registerRegenerationTarget - Storage path:
/favicon/ - Pure-JS ICO encoder:
src/mu-plugins/flexweg-favicon/icoEncoder.ts - Translations: 7 locales
Continue
- Site features: Favicons + PWA — visitor / installed-app perspective
- Hooks reference