X Xerobit

Favicon ICO — Create and Implement .ico Files for Your Website

The favicon.ico file is the icon shown in browser tabs, bookmarks, and history. Here's how to create an ICO file, what sizes it needs, and how to add it to your HTML properly.

Mian Ali Khalid · · 5 min read
Use the tool
Favicon Generator
Generate favicon package from any image or emoji. Multiple sizes (16, 32, 48, 180, 192, 512), manifest.json, and HTML snippet.
Open Favicon Generator →

The favicon.ico file is a special image format that browsers look for automatically at the root of your website. It appears in browser tabs, bookmarks, search results, and desktop shortcuts. Getting it right requires understanding the ICO format, required sizes, and correct HTML implementation.

Use the Favicon Generator to create favicon.ico files from any image.

What is the ICO format?

ICO is a container format — a single .ico file can embed multiple images at different sizes. Browsers pick the most appropriate size for the context (16×16 for tabs, 32×32 for taskbars, 48×48 for desktop shortcuts).

A complete favicon.ico for modern browsers typically contains:

SizePurpose
16×16Browser tab (primary use)
32×32Taskbar shortcut, Windows desktop
48×48Windows shortcut
64×64High-DPI contexts

Not all sizes are required. At minimum, 16×16 and 32×32 cover most cases.

How browsers find your favicon

Browsers check two places:

1. Default location (no HTML needed):

https://yourdomain.com/favicon.ico

Browsers automatically request this URL for every site. If the file exists, it loads. This is the safest approach — works even in contexts where your <head> tags aren’t loaded.

2. HTML link element:

<head>
  <link rel="icon" href="/favicon.ico" type="image/x-icon">
</head>

Use the HTML approach when:

  • Your favicon isn’t at the root (e.g., it’s in /static/icons/)
  • You want to specify different icons for different pages
  • You’re using multiple formats (SVG, PNG, ICO)

Creating favicon.ico

From a PNG (command line, ImageMagick)

# Single-size ICO:
convert favicon-32.png favicon.ico

# Multi-size ICO (recommended):
convert favicon-16.png favicon-32.png favicon-48.png favicon.ico

# From a high-res source image:
convert source-512.png -define icon:auto-resize=64,48,32,16 favicon.ico

Node.js (to-ico)

import toIco from 'to-ico';
import fs from 'fs/promises';

async function createFavicon() {
  // Read PNG files at different sizes:
  const images = await Promise.all([
    fs.readFile('favicon-16.png'),
    fs.readFile('favicon-32.png'),
    fs.readFile('favicon-48.png'),
  ]);
  
  // Combine into ICO:
  const ico = await toIco(images);
  await fs.writeFile('favicon.ico', ico);
  console.log('favicon.ico created');
}

createFavicon();

Python (Pillow)

from PIL import Image

def create_favicon(source_path, output_path='favicon.ico'):
    """Create multi-size favicon.ico from a source image."""
    img = Image.open(source_path).convert('RGBA')
    sizes = [(16, 16), (32, 32), (48, 48), (64, 64)]
    
    # Generate each size:
    resized_images = []
    for size in sizes:
        resized = img.resize(size, Image.LANCZOS)
        resized_images.append(resized)
    
    # Save as ICO (first image sets the container, rest are added automatically):
    resized_images[0].save(
        output_path,
        format='ICO',
        sizes=sizes,
        append_images=resized_images[1:]
    )
    print(f'Created {output_path}')

create_favicon('logo-512.png')

Complete favicon HTML setup

Modern browsers support multiple favicon formats. A robust setup:

<head>
  <!-- ICO: fallback for all browsers including IE -->
  <link rel="icon" href="/favicon.ico" sizes="any">
  
  <!-- SVG: modern browsers, scales to any size -->
  <link rel="icon" href="/favicon.svg" type="image/svg+xml">
  
  <!-- PNG: for specific sizes when SVG isn't supported -->
  <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: iOS home screen icon -->
  <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
  
  <!-- Web manifest: Android and PWA -->
  <link rel="manifest" href="/site.webmanifest">
</head>

Browser priority (highest to lowest):

  1. SVG (Chrome, Firefox, modern browsers)
  2. PNG at matching size
  3. ICO (fallback)

Next.js (App Router)

// app/favicon.ico — just place the file here, Next.js handles it automatically

// Or use metadata API for more control:
// app/layout.tsx
export const metadata = {
  icons: {
    icon: '/favicon.ico',
    shortcut: '/favicon-16x16.png',
    apple: '/apple-touch-icon.png',
  },
};

Vite / React

<!-- index.html -->
<head>
  <link rel="icon" type="image/x-icon" href="/favicon.ico">
  <link rel="icon" type="image/svg+xml" href="/favicon.svg">
</head>

Place favicon.ico in /public/ — Vite serves it at the root automatically.

Astro

<!-- src/layouts/Base.astro -->
<head>
  <link rel="icon" href="/favicon.ico" sizes="any">
  <link rel="icon" href="/favicon.svg" type="image/svg+xml">
</head>

Place icons in /public/ — Astro copies them to the build output root.

Testing favicon implementation

Check browser tab: Open your page. The icon should appear in the browser tab within a few seconds.

Check different contexts:

  • Bookmark the page — does the icon appear in bookmarks?
  • On Windows: create a desktop shortcut — does the correct size display?
  • On iOS: add to home screen — does the apple-touch-icon appear?

Browser cache: Browsers aggressively cache favicons. After updating, clear cache or use incognito mode to see changes.

Favicon checker tools:

  • Browser DevTools → Network tab → filter by “favicon” to see which file loaded
  • Check for 404 errors in the console

Common favicon mistakes

Missing favicon.ico at root: Even with <link> tags, some contexts (RSS readers, API clients, link preview crawlers) only check /favicon.ico. Always keep one there.

Wrong MIME type: Serving ICO as image/ico instead of image/x-icon can cause some browsers to reject it. Use image/x-icon or image/vnd.microsoft.icon.

Too small source: Designing at 16×16 makes the icon look pixel-art at larger sizes. Design at 512×512 and downscale — let the resize algorithm do the work.

Not handling dark mode: A white logo on white favicon is invisible on light-mode browsers. For SVG favicons, you can use CSS prefers-color-scheme to adapt:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
  <style>
    @media (prefers-color-scheme: dark) {
      path { fill: white; }
    }
    path { fill: black; }
  </style>
  <path d="M16 2..."/>
</svg>

Related posts

Related tool

Favicon Generator

Generate favicon package from any image or emoji. Multiple sizes (16, 32, 48, 180, 192, 512), manifest.json, and HTML snippet.

Written by Mian Ali Khalid. Part of the Frontend & Design pillar.