
Favicon Optimization for Different Browsers in 2025
The complete guide to creating favicons that work flawlessly across all browsers and devices

Complete technical guide for developers to integrate Logo Foundry assets into their projects efficiently
Once you've generated your logo assets with Logo Foundry, implementing them properly is crucial for optimal performance and user experience. This guide covers everything from basic HTML setup to advanced optimization techniques.
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Primary favicon (modern browsers) -->
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<!-- PNG fallbacks -->
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<!-- Apple Touch Icon -->
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<!-- Web App Manifest -->
<link rel="manifest" href="/site.webmanifest">
<!-- Safari Pinned Tab -->
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
<!-- Windows Tiles -->
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
</head>
</html>Create /public/site.webmanifest:
{
"name": "Your App Name",
"short_name": "YourApp",
"description": "Your app description",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#000000",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/android-chrome-maskable-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/android-chrome-maskable-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}// app/layout.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Your App',
description: 'Your app description',
icons: {
icon: [
{ url: '/favicon.svg', type: 'image/svg+xml' },
{ url: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
{ url: '/favicon-16x16.png', sizes: '16x16', type: 'image/png' },
],
apple: [
{ url: '/apple-touch-icon.png', sizes: '180x180' },
],
other: [
{ rel: 'mask-icon', url: '/safari-pinned-tab.svg', color: '#5bbad5' },
],
},
manifest: '/site.webmanifest',
themeColor: '#ffffff',
}// pages/_app.tsx
import Head from 'next/head'
function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<meta name="theme-color" content="#ffffff" />
</Head>
<Component {...pageProps} />
</>
)
}<!-- public/index.html -->
<link rel="icon" href="%PUBLIC_URL%/favicon.svg" type="image/svg+xml">
<link rel="icon" type="image/png" sizes="32x32" href="%PUBLIC_URL%/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="%PUBLIC_URL%/favicon-16x16.png">
<link rel="apple-touch-icon" sizes="180x180" href="%PUBLIC_URL%/apple-touch-icon.png">
<link rel="manifest" href="%PUBLIC_URL%/site.webmanifest">
<meta name="theme-color" content="#ffffff">---
// src/layouts/Layout.astro
export interface Props {
title: string;
}
const { title } = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="description" content="Astro description">
<meta name="viewport" content="width=device-width">
<!-- Favicons -->
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
<title>{title}</title>
</head>
<body>
<slot />
</body>
</html><!-- app.html -->
<head>
%sveltekit.head%
<!-- Favicons -->
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
<meta name="theme-color" content="#ffffff">
</head>// Change favicon based on application state
function updateFavicon(type) {
const link = document.querySelector("link[rel~='icon']");
if (link) {
switch(type) {
case 'notification':
link.href = '/favicon-notification.svg';
break;
case 'error':
link.href = '/favicon-error.svg';
break;
default:
link.href = '/favicon.svg';
}
}
}
// Usage examples
updateFavicon('notification'); // Show notification badge
updateFavicon('error'); // Show error state
updateFavicon('default'); // Reset to normal// Detect and respond to color scheme changes
function updateFaviconForTheme() {
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const link = document.querySelector("link[rel~='icon']");
if (link) {
link.href = isDark ? '/favicon-dark.svg' : '/favicon-light.svg';
}
}
// Listen for theme changes
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', updateFaviconForTheme);
// Set initial favicon
updateFaviconForTheme();<!-- Preload main favicon -->
<link rel="preload" href="/favicon.svg" as="image" type="image/svg+xml">
<!-- Preload for faster PWA installation -->
<link rel="preload" href="/android-chrome-512x512.png" as="image"><!-- Use CDN for global distribution -->
<link rel="icon" href="https://cdn.yourdomain.com/favicon.svg" type="image/svg+xml">
<!-- Fallback to local files -->
<link rel="alternate icon" href="/favicon.ico">// Load additional icon sizes on demand
const loadAdditionalIcons = () => {
const sizes = ['96x96', '128x128', '256x256'];
sizes.forEach(size => {
const link = document.createElement('link');
link.rel = 'icon';
link.type = 'image/png';
link.sizes = size;
link.href = `/favicon-${size}.png`;
document.head.appendChild(link);
});
};
// Load when needed
if ('serviceWorker' in navigator) {
loadAdditionalIcons();
}<meta http-equiv="Content-Security-Policy"
content="img-src 'self' data: https://cdn.yourdomain.com;"><!-- For externally hosted assets -->
<link rel="icon"
href="https://cdn.example.com/favicon.svg"
type="image/svg+xml"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous">// Jest test for favicon presence
describe('Favicon Tests', () => {
test('should have primary favicon', () => {
const favicon = document.querySelector('link[rel="icon"]');
expect(favicon).toBeTruthy();
expect(favicon.href).toMatch(/favicon\.(svg|png|ico)$/);
});
test('should have Apple Touch Icon', () => {
const appleIcon = document.querySelector('link[rel="apple-touch-icon"]');
expect(appleIcon).toBeTruthy();
expect(appleIcon.sizes).toBe('180x180');
});
});// Feature detection
const browserSupport = {
svg: (() => {
const div = document.createElement('div');
div.innerHTML = '<svg/>';
return div.firstChild && div.firstChild.namespaceURI === 'http://www.w3.org/2000/svg';
})(),
webp: (() => {
const canvas = document.createElement('canvas');
return canvas.toDataURL('image/webp').indexOf('webp') > -1;
})()
};
// Use appropriate favicon format
if (browserSupport.svg) {
// Use SVG favicon
} else {
// Fallback to PNG/ICO
}Debugging steps:
Solution:
<!-- Specify exact size matching the file -->
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">Server configuration (Apache):
AddType image/x-icon .ico
AddType image/vnd.microsoft.icon .icoServer configuration (Nginx):
location ~ \.(ico)$ {
add_header Content-Type image/x-icon;
expires 1y;
add_header Cache-Control "public, immutable";
}public/
├── favicon.svg # Primary favicon
├── favicon.ico # Legacy support
├── favicon-16x16.png # Small PNG
├── favicon-32x32.png # Standard PNG
├── apple-touch-icon.png # iOS devices
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── android-chrome-maskable-192x192.png
├── android-chrome-maskable-512x512.png
├── safari-pinned-tab.svg # Safari pinned tabs
├── mstile-144x144.png # Windows tiles
├── site.webmanifest # PWA manifest
└── browserconfig.xml # Windows configuration// webpack.config.js
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{ from: 'src/assets/favicons', to: 'favicons' },
{ from: 'src/assets/manifest.json', to: 'manifest.json' },
],
}),
],
};// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
publicDir: 'public',
build: {
rollupOptions: {
input: {
main: 'index.html',
manifest: 'public/site.webmanifest',
}
}
}
})Logo Foundry provides ready-to-use code snippets:
<!-- Copy directly from Logo Foundry output -->
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<!-- ... additional tags -->Next.js metadata:
// Copy from Logo Foundry's Next.js snippet
export const metadata: Metadata = {
// ... favicon configuration
}Vite/React public folder:
/public/index.html with provided snippets# Nginx configuration for favicon caching
location ~* \.(ico|png|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Vary "Accept-Encoding";
# Enable gzip for SVG
gzip on;
gzip_types image/svg+xml;
}// Express.js with HTTP/2 push
app.get('/', (req, res) => {
// Push critical favicon resources
res.push('/favicon.svg', {
'content-type': 'image/svg+xml'
});
res.push('/favicon-32x32.png', {
'content-type': 'image/png'
});
res.sendFile(path.join(__dirname, 'index.html'));
});// service-worker.js
const CACHE_NAME = 'favicon-cache-v1';
const urlsToCache = [
'/favicon.svg',
'/favicon-32x32.png',
'/favicon-16x16.png',
'/apple-touch-icon.png',
'/site.webmanifest'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(urlsToCache))
);
});// Monitor favicon load times
const trackFaviconLoad = () => {
const favicon = document.querySelector('link[rel="icon"]');
if (favicon) {
const img = new Image();
img.onload = () => {
// Track successful load
analytics.track('favicon_loaded', {
format: favicon.href.split('.').pop(),
loadTime: performance.now()
});
};
img.onerror = () => {
// Track load failures
analytics.track('favicon_failed', {
url: favicon.href
});
};
img.src = favicon.href;
}
};// Test different favicon variants
const faviconVariants = [
'/favicon-v1.svg',
'/favicon-v2.svg'
];
const selectedFavicon = faviconVariants[Math.floor(Math.random() * faviconVariants.length)];
document.querySelector('link[rel="icon"]').href = selectedFavicon;
// Track performance metrics
analytics.track('favicon_variant', {
variant: selectedFavicon,
userAgent: navigator.userAgent
});import { useEffect } from 'react';
interface UseFaviconOptions {
default: string;
notification?: string;
error?: string;
}
export const useFavicon = (options: UseFaviconOptions) => {
const setFavicon = (type: keyof UseFaviconOptions = 'default') => {
const link = document.querySelector("link[rel~='icon']") as HTMLLinkElement;
if (link && options[type]) {
link.href = options[type];
}
};
useEffect(() => {
setFavicon('default');
}, []);
return { setFavicon };
};
// Usage
const { setFavicon } = useFavicon({
default: '/favicon.svg',
notification: '/favicon-notification.svg',
error: '/favicon-error.svg'
});
// Show notification
setFavicon('notification');// composables/useFavicon.ts
import { ref } from 'vue'
export const useFavicon = () => {
const currentFavicon = ref('/favicon.svg')
const setFavicon = (url: string) => {
const link = document.querySelector("link[rel~='icon']") as HTMLLinkElement
if (link) {
link.href = url
currentFavicon.value = url
}
}
return {
currentFavicon,
setFavicon
}
}✅ All icon files are in correct directories
✅ File paths match HTML references
✅ MIME types configured on server
✅ Manifest file is valid JSON
✅ Meta tags are properly formatted
✅ Cache headers are configured
✅ Favicon appears in browser tabs
✅ Bookmark icons display correctly
✅ PWA installation works on mobile
✅ Apple devices show touch icons
✅ Windows tiles appear correctly
✅ Loading performance is acceptable
<!-- Add version parameter for cache busting -->
<link rel="icon" href="/favicon.svg?v=1.2.0" type="image/svg+xml">// CI/CD validation script
const validateFavicons = () => {
const requiredFiles = [
'/favicon.svg',
'/favicon-32x32.png',
'/favicon-16x16.png',
'/apple-touch-icon.png',
'/site.webmanifest'
];
requiredFiles.forEach(file => {
if (!fs.existsSync(`./public${file}`)) {
throw new Error(`Missing favicon file: ${file}`);
}
});
};Proper implementation of logo assets requires attention to detail and understanding of platform differences. By following this guide and using Logo Foundry's generated code snippets, you can ensure your icons display perfectly across all browsers and devices.
Remember to test your implementation thoroughly and monitor performance metrics to ensure the best user experience.
Need help generating developer-ready assets? Try Logo Foundry for complete asset packages with implementation code included.

The complete guide to creating favicons that work flawlessly across all browsers and devices

Master the art of creating perfect logo assets for all platforms with our comprehensive guide

Announcing the launch of Logo Foundry, the developer's solution to multi-platform logo deployment challenges
Join the community
Subscribe to our newsletter for the latest news and updates