fix(branding): serve favicon via backend route so Safari picks it up

Safari requests /favicon.ico and /apple-touch-icon*.png at the site root and
is unreliable about honouring JS-injected <link rel=icon>, so an admin-set
favicon never showed there (index.html only ships /favicon-32x32.png; a bare
/favicon.ico 404'd).

- Backend: GET /favicon.ico + /apple-touch-icon(.png|-precomposed.png) resolve
  the configured branding_favicon_url (redirect to its /uploads path or the
  absolute URL), falling back to the bundled /favicon-32x32.png.
- nginx: exact-match (=) locations proxy those paths to the backend, winning
  over the static-asset regex that previously served them from the build dir.
- DynamicFavicon also emits an apple-touch-icon link (belt-and-braces).

Requires a frontend image REBUILD (nginx.conf change) in addition to backend.
This commit is contained in:
Luca
2026-06-03 17:35:32 +02:00
parent 5e79c69cda
commit db3e3270f3
3 changed files with 71 additions and 1 deletions
@@ -35,8 +35,16 @@ export const DynamicFavicon: React.FC = () => {
link.rel = 'icon';
if (ext && typeByExt[ext]) link.type = typeByExt[ext];
link.href = href;
document.head.appendChild(link);
// Safari uses apple-touch-icon for bookmarks / home-screen and is
// unreliable about JS-injected rel="icon". The backend /favicon.ico +
// /apple-touch-icon routes are the primary mechanism; this is
// belt-and-braces for browsers that do read the DOM link.
const appleLink = document.createElement('link');
appleLink.rel = 'apple-touch-icon';
appleLink.href = href;
document.head.appendChild(appleLink);
}
}, [settings?.branding_favicon_url]);