
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
Different tools export different folder layouts. The standalone Favicon Generator supplies files at the ZIP root, including favicon.ico, favicon-32x32.png, apple-touch-icon.png, site.webmanifest and integration-guide.html. The server upload workflow organizes image files under icons/ and social/, and code under snippets/.
Use the downloadable favicon example to follow this guide without an account. Extract the ZIP before copying files; a link to the ZIP cannot act as an icon URL.
For the following example, copy the favicon PNGs, ICO and Apple Touch PNG into your website's public/icons/ directory. Put the standalone manifest at public/site.webmanifest. These locations map to /icons/... and /site.webmanifest on a conventional static deployment.
If you move files into icons/, update every icon src in the manifest to the new public URL. Relative manifest paths resolve relative to the manifest URL, not the HTML page. For a subpath deployment, include the actual base path in the public URLs.
Map the downloaded files to public URLs before adding declarations.
<link rel="icon" href="/icons/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">These links use the actual standalone export filenames. Add an SVG link only if your chosen workflow supplied an SVG. Do not add Safari pinned-tab, Windows or social-image links merely because they appear in an old checklist: confirm the files exist and that the feature is relevant.
Copy site.webmanifest from the standalone favicon package to the public root and update each image src to its actual location. A path relative to the manifest is not relative to the HTML page. The manifest describes icons and launch settings; it does not implement offline caching. For standard and maskable examples, see the PWA guide.
The examples below use the same exported files. They are manual integration instructions, not additional framework packages generated by LogoFoundry. Except for SvelteKit's asset placeholder, the URLs assume a deployment at the domain root; adapt them for your project's base path.
Choose one icon configuration and inspect the final head. File-based metadata can take precedence over metadata configuration.
Use the metadata configuration below in your app layout, with the images still under public/icons/. Alternatively, use Next.js's app/favicon.ico, app/icon.png and app/apple-icon.png file conventions. Avoid keeping conflicting file-based and metadata-based icons. See Next.js icon conventions and metadata configuration.
import type { Metadata } from 'next';
export const metadata: Metadata = {
icons: {
icon: [
{ url: '/icons/favicon.ico' },
{ url: '/icons/favicon-32x32.png', type: 'image/png', sizes: '32x32' },
],
apple: [{ url: '/icons/apple-touch-icon.png', sizes: '180x180' }],
},
manifest: '/site.webmanifest',
};The manifest is an icon and launch configuration file; it does not implement offline caching or prove installability. Native iOS AppIcon.appiconset and Android res/ exports belong in an app project, not your HTML head.
The original heading is retained; Pages Router is not restricted to Next.js 12 and earlier. For an existing Pages Router project, keep the files in public/icons/ and add the links with next/head. Put shared declarations in pages/_app.tsx; remove conflicting declarations from individual pages.
import Head from 'next/head';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Head>
<link rel="icon" href="/icons/favicon.ico" key="favicon" />
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
</Head>
<Component {...pageProps} />
</>
);
}Copy the icons to public/icons/ and the manifest to public/site.webmanifest. Edit the project-root index.html, not a React component or public/index.html. This also applies to a Vue project that uses Vite's HTML entry. See Vite public assets.
<!-- Inside the existing index.html head -->
<link rel="icon" href="/icons/favicon.ico">
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">Vite copies public files without hashing their filenames. With a configured base, use the appropriate base-prefixed URLs in both the HTML and manifest. Check the built deployment, including a nested route, rather than assuming a working development-server URL proves the final path.
For a maintained older Create React App project, follow that project’s HTML entry and public-path conventions; do not copy the Vite root-entry rule into it unchanged.
Place the same files under public/. Add the declarations to the existing <head> in a shared layout such as src/layouts/Layout.astro, so pages that use that layout receive the links. Astro's public-directory documentation explains how these files are copied.
<!-- Inside the shared .astro layout head -->
<link rel="icon" href="/icons/favicon.ico">
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">Avoid adding another copy in every page that already uses the shared layout. For an Astro site deployed under a subpath, include that base path in the public URLs and check manifest references too.
Use static/icons/ for the images and static/site.webmanifest for the manifest. Add these links inside src/app.html's existing head, retaining %sveltekit.head% and the rest of the template. The SvelteKit project structure documents the asset placeholder.
<link rel="icon" href="%sveltekit.assets%/icons/favicon.ico">
<link rel="apple-touch-icon" href="%sveltekit.assets%/icons/apple-touch-icon.png">
<link rel="manifest" href="%sveltekit.assets%/site.webmanifest">The placeholder belongs to app.html; do not paste it into the JSON manifest. Set manifest image URLs relative to the manifest's deployed location, or use the actual public base path. Confirm the production adapter serves every file.
Keep a working icon in the initial HTML before adding optional client-side behavior.
An application may use a different icon for an unread state. Keep a normal icon in the initial HTML, and change one explicitly managed link after the application state changes. This optional example assumes you created and published both PNG files separately; they are not extra assets in the standard ZIP.
<link id="site-favicon" rel="icon" type="image/png" href="/icons/favicon-32x32.png">function setUnreadIcon(unread) {
const icon = document.querySelector('link#site-favicon');
if (icon) {
icon.setAttribute('href', unread
? '/icons/favicon-unread.png'
: '/icons/favicon-32x32.png');
}
}Use the actual deployment base path. If a framework owns the head, integrate the change with that framework's state/head mechanism so subsequent navigation does not undo it. Avoid repeatedly appending icon links. A dynamic tab icon does not update the installed PWA icon or Google's search favicon. For theme-based artwork, see the dark-mode SVG example.
A self-contained SVG can use prefers-color-scheme; separate files are another option when supported. Prepare the actual variants and test the browser’s chosen URL. See the dark-mode example. Runtime tab changes do not update the installed-app or search icon.
Inspect the actual network requests and smallest image before adding hints or scripts.
Do not preload every icon or claim that preloading a 512px PNG guarantees faster installation. Add a preload only after measuring the relevant page and request.
A CDN-hosted icon must return the intended file publicly, with a suitable image content type and the correct URL in the head. Inspect CDN caching separately; multiple icon declarations do not guarantee a fixed fallback order.
A service-worker API check does not tell you which favicon sizes the browser needs. Avoid appending every size dynamically as a general optimization; declare the files needed by actual display contexts.
Use artwork you trust and inspect SVG before publishing it. If your site uses a Content Security Policy or a separate asset host, inspect console errors and the policy that applies to the image and manifest requests; avoid weakening a site-wide policy just to hide a broken URL. Public favicon and share-image URLs must return image files without requiring a private account session.
Inspect browser console errors and the policies applicable to image and manifest requests. Permit only the asset locations your deployment actually uses, and do not replace a site-wide CSP with a copied partial policy.
Do not paste an example checksum into a live declaration or assume it proves favicon integrity. Verify known file checksums in your build or release process and inspect the deployed bytes; any browser integrity mechanism must be supported for the exact resource type in use.
Validate files, declarations and actual browser displays as separate steps.
A DOM assertion only checks the declaration; add real file-response checks to the deployment test. For example, in a test environment with the rendered document:
const icon = document.querySelector('link[rel="icon"]');
if (!icon?.getAttribute('href')) throw new Error('Missing icon URL');
const touch = document.querySelector('link[rel="apple-touch-icon"]');
if (!touch?.getAttribute('href')) throw new Error('Missing Apple Touch URL');Then request the URLs and verify content types and image dimensions rather than accepting an HTML fallback with status 200.
Creating an SVG element or encoding a WebP canvas does not detect support for that format as a favicon. Test the browser/version and target surface directly, recording which public file was selected.
Work from the public URL and response toward the design, not the other way round.
src URLs return the correct PNGs.Google search display is a separate process. Follow Google's favicon guidance; a correct tag does not guarantee immediate display. Report reproducible export or documentation issues through About and contact.
Check the actual file dimensions and the sizes attribute. Avoid duplicate framework declarations and distinguish small browser icons from manifest launcher images.
Confirm the file is a valid ICO, not a renamed PNG or HTML fallback. Inspect server content types and routing. Do not add duplicate Content-Type headers or long immutable caching to a frequently replaced, unversioned URL.
Keep the package layout and its public installation layout distinct.
For a public/-based project, a small directory layout is enough:
public/
icons/
favicon.ico
favicon-16x16.png
favicon-32x32.png
apple-touch-icon.png
android-chrome-192x192.png
android-chrome-512x512.png
site.webmanifestThis is an installation layout, not a claim that all tools export the same ZIP. In SvelteKit, use static/ instead of public/. If the manifest is at the root, a relative icons/android-chrome-192x192.png refers to the icon folder; a bare android-chrome-192x192.png does not. Keep icon references and actual filenames together during review.
Use the filenames your package actually contains. If you rename or move a file, update HTML and manifest references together. Native app packages belong in their app project, not the website head.
For the frameworks above, use the documented public/static directory first; a second custom copy script can create stale duplicates. After building, check the output or the running production adapter for the icon files and manifest. Ensure an SPA fallback rule does not rewrite missing image requests to index.html, and confirm root and nested URLs use the same intended icon paths.
In a custom Webpack project, copy public icon files as part of the project’s established asset pipeline and check the resulting deployment paths. Do not copy a second directory over the framework’s output or assume the ZIP includes a ready-made Webpack integration.
Use Vite’s public directory for unchanged icon and manifest files. Do not add the manifest JSON as an HTML Rollup entry. Check configured base, the built HTML and relative icon URLs inside the manifest.
Select the correct tool, inspect the downloaded package, then integrate and test it.
Use the Favicon Generator for its browser export, or the upload workflow for its server package. Different tools produce different layouts; inspect the result before copying.
<link rel="icon" href="/icons/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">These links use the actual standalone export filenames. Add an SVG link only if your chosen workflow supplied an SVG. Do not add Safari pinned-tab, Windows or social-image links merely because they appear in an old checklist: confirm the files exist and that the feature is relevant.
Use the metadata configuration below in your app layout, with the images still under public/icons/. Alternatively, use Next.js's app/favicon.ico, app/icon.png and app/apple-icon.png file conventions. Avoid keeping conflicting file-based and metadata-based icons. See Next.js icon conventions and metadata configuration.
import type { Metadata } from 'next';
export const metadata: Metadata = {
icons: {
icon: [
{ url: '/icons/favicon.ico' },
{ url: '/icons/favicon-32x32.png', type: 'image/png', sizes: '32x32' },
],
apple: [{ url: '/icons/apple-touch-icon.png', sizes: '180x180' }],
},
manifest: '/site.webmanifest',
};The manifest is an icon and launch configuration file; it does not implement offline caching or prove installability. Native iOS AppIcon.appiconset and Android res/ exports belong in an app project, not your HTML head.
Treat the upload quality report as image-statistics hints and file checks, not WCAG measurement or platform certification. Check the actual files and target browsers yourself.
Review caching independently from artwork and icon declarations.
Keep a detailed source file for editing and publish only the formats and sizes needed by the target contexts. Optimize a copy, then compare the 16px and 32px results: a smaller file that loses the symbol's cutout is not an improvement. PNG compression does not have a universal JPEG-style quality percentage.
Serve files with suitable image content types. If you use a CDN or service worker, inspect its cache independently from the browser cache. For versioned assets, update HTML and manifest references in the same release and keep the previous files reachable while old documents are cached. A version query can help identify which response is being served, but does not force every browser or installed app to update its icon.
Do not preload every icon as a general performance rule. First inspect actual network requests and page performance. Adding scripts to repeatedly change the favicon also does not solve a wrong deployment path.
Do not use the old Express res.push example as a deployment recipe or assume every server supports it. Use the project’s actual delivery stack and measured requests; icon setup does not require server push.
If the project already uses a service worker, inspect which cache serves the icon and manifest. Update the application’s established cache versioning and fetch strategy together. Adding only an install-time cache.addAll does not implement a complete offline or update workflow.
Keep implementation checks separate from traffic or engagement claims.
Inspect request status and transfer timing in the browser or deployment monitoring you already operate. Triggering a new Image() request does not prove how the browser’s favicon loader behaved; performance.now() by itself is not an elapsed icon-load duration.
Compare design variants in previews or controlled browser checks first. Do not rotate the search favicon URL for every visitor or claim a traffic improvement without a valid measurement setup.
These are optional client-side examples for a link you manage yourself. Create all referenced image files and use the actual base path. Call hooks/composables in the framework’s component lifecycle; reconcile them with any framework-managed head.
Declare one existing icon link with id="site-favicon". This hook changes it after rendering and restores its previous URL when the effect is cleaned up:
import { useEffect } from 'react';
export function useFavicon(href: string) {
useEffect(() => {
const link = document.querySelector<HTMLLinkElement>('link#site-favicon');
if (!link) return;
const previous = link.getAttribute('href');
link.setAttribute('href', href);
return () => {
if (previous !== null) link.setAttribute('href', previous);
};
}, [href]);
}Call it inside a component with the URL derived from application state. It does not create notification artwork or change installed/search icons.
The same existing-link pattern can be used inside Vue component setup. The initial DOM lookup runs after mount:
import { onMounted, onBeforeUnmount, watch, type Ref } from 'vue';
export function useFavicon(href: Ref<string>) {
let link: HTMLLinkElement | null = null;
let previous: string | null = null;
onMounted(() => {
link = document.querySelector<HTMLLinkElement>('link#site-favicon');
previous = link?.getAttribute('href') ?? null;
link?.setAttribute('href', href.value);
});
watch(href, value => link?.setAttribute('href', value));
onBeforeUnmount(() => {
if (link && previous !== null) link.setAttribute('href', previous);
});
}Use a real published URL and check navigation behavior if the framework also manages the head.
Check the built deployment at the homepage and a nested route.
Before releasing, open the production build at the homepage and a nested URL. Confirm that the icon and manifest paths work without signing in, the manifest references real files, and the head contains the intended declarations. Test relevant browser tabs, bookmarks and Home Screen installation separately. Keep the original artwork and a copy of the deployed package so a later icon change can be compared or rolled back.
Request the deployed icons and manifest without signing in. Inspect the final head and test relevant tabs, bookmarks and Home Screen installation separately. Do not mark unused legacy tile features as a universal requirement.
Keep editable artwork and a known deployed package for comparison or rollback.
Keep a detailed source file for editing and publish only the formats and sizes needed by the target contexts. Optimize a copy, then compare the 16px and 32px results: a smaller file that loses the symbol's cutout is not an improvement. PNG compression does not have a universal JPEG-style quality percentage.
Serve files with suitable image content types. If you use a CDN or service worker, inspect its cache independently from the browser cache. For versioned assets, update HTML and manifest references in the same release and keep the previous files reachable while old documents are cached. A version query can help identify which response is being served, but does not force every browser or installed app to update its icon.
Do not preload every icon as a general performance rule. First inspect actual network requests and page performance. Adding scripts to repeatedly change the favicon also does not solve a wrong deployment path.
In CI, verify the files this project actually deploys and the paths declared in its HTML and manifest. File existence alone is not enough: request the deployed URLs and inspect their response bodies and dimensions. Do not hard-code nonexistent SVG or platform files as mandatory outputs.
Use the framework’s actual public-file conventions, keep declarations aligned with the downloaded package and verify the deployed result. These steps do not guarantee identical rendering in every browser. Report reproducible export or documentation issues through About and contact.
Contributor byline for LogoFoundry guides. For corrections or questions about this article, use the contact details on the About page.
How we maintain these guides
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