Finding the largest favicon with js

As part of an audit I wanted to fetch the highest quality favicon for every page that we check. It turned out to be slightly more complicated than I thought:

These stackoverflow posts suggest a few ways to get the favicon, but they all share a few issues:

  • It depends on a 3rd party (eg Google)

  • It ignores the default /favicon.ico, or uses it incorrectly

  • It returns the first favicon, instead of the largest one

How the browser chooses a favicon

The standard way to include a favicon is with a link tag <link rel="icon".. You can use multiple link tags to specify different sizes, for example:

<link rel="icon" sizes="16x16" href="/favicon_16.png">
<link rel="icon" sizes="32x32" href="/favicon_32.png">

Another popular variant is <link rel="shortcut icon"..

<link rel="shortcut icon" href="/favicon_32.png">

If none of these tags are present the browser will make a request to the /favicon.ico file at the root directory. Some servers are badly configured though and will return an 200 OK status even if the file is not present, so to be sure you have to check that the file is indeed an image.

The solution

The following codes combines all these factors, and loops through the available favicons to return the largest one.

// Get the largest favicon in the current document, or false if none is found.
let getLargestFavicon = async () => {
  let getSize = el => {
    return (el.sizes[0] && parseInt(el.sizes[0], 10)) || 0;
  };
  let favicons = [
    ...document.querySelectorAll('link[rel="shortcut icon"],link[rel="icon"]')
  ].sort((a, b) => {
    return getSize(b) - getSize(a);
  });
  if (favicons.length > 0) {
    return favicons[0].href;
  }
  // no favicon is specified in the meta tags, lets try the default /favicon.ico
  let defaultLocation = document.location.origin + "/favicon.ico";
  let r = await fetch(defaultLocation);
  if (r.ok) {
    let t = await r.blob();
    if (t.type.indexOf("image") !== -1) {
      return defaultLocation;
    }
  }
  return false;
};

Written by Loftie Ellis (follow on Twitter)