How to Add a Favicon to an HTML Page
Add a favicon with one HTML link, choose the right format and path, and fix common browser caching and deployment problems.
A favicon is the small icon shown in a browser tab, bookmark list, history, and other browser interface areas. Add one by placing an icon file in your deployed site and referencing it from the document <head>:
<link rel="icon" href="/favicon.ico" type="image/x-icon">
The href must resolve to the real public URL of the icon. A conventional /favicon.ico at the site root is also discovered automatically by many browsers, but an explicit link works for any supported filename or format.
1. Add the basic favicon
- Create or export an icon asset, such as
favicon.icoorfavicon-32x32.png. - Copy it to the directory your server exposes at the site root. In many frameworks this is called
publicorstatic. - Add a
linkelement inside every page’s<head>, or in the shared HTML layout. - Deploy both the HTML and the asset, then open the icon URL directly to verify it.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>My site</title>
<link rel="icon" href="/favicon.ico" type="image/x-icon">
</head>
<body>
<h1>Hello</h1>
</body>
</html>
MDN documents the same approach: save a supported .ico, .gif, or .png file and add a <link> element in the HTML head (MDN favicon guidance).
2. Choose a format and provide multiple sizes
ICO is a useful compatibility fallback. PNG is simple to generate and lets you publish explicit dimensions. SVG can work in browsers that support SVG favicons, but provide a raster fallback when you need broader or older-browser coverage.
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<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="icon" type="image/svg+xml" href="/favicon.svg">
When several icon links exist, browsers can use media, type, and sizes as selection hints (MDN rel reference). Use square source artwork and check it at small sizes; fine detail disappears in a 16-pixel tab.
Dark-mode variants
You can offer different icons with a media hint:
<link rel="icon" href="/favicon-light.png" media="(prefers-color-scheme: light)">
<link rel="icon" href="/favicon-dark.png" media="(prefers-color-scheme: dark)">
Browser support and update timing vary, so keep a normal fallback link as well.
3. Get the path right
| Reference | Resolves from | Example |
|---|---|---|
| Root-relative | Site origin | /images/favicon.png |
| Document-relative | Current page URL | images/favicon.png |
| Absolute URL | Explicit origin | https://cdn.example.com/favicon.png |
For a page at /docs/setup/, images/favicon.png points under that page’s URL path, while /images/favicon.png always starts at the site root. Root-relative paths are usually safest when one asset is shared by the whole site.
Case matters on many production servers. /Favicon.png and /favicon.png may be different files. Confirm the deployed filename, not only the local filename.
4. Add a favicon in common setups
Plain HTML
Put the file beside your deployed root (for example, public/favicon.ico if public becomes /) and use the basic link.
React, Vue, or another SPA
Place the asset in the framework’s public/static directory so the build copies it without importing it into JavaScript. Put the link in the root HTML template, commonly index.html.
Server-rendered templates
Add the link to the shared layout or base template. Do not rely on a component that only renders after JavaScript runs; browsers inspect the document head during navigation.
Subdirectory deployments
If the entire site is served under /app/, a leading slash may point to the domain root instead of your application. Use the framework’s base-path helper or a correctly prefixed URL such as /app/favicon.ico.
5. PWA icons are separate
A favicon identifies a document in browser UI. A progressive web app also needs a web app manifest for installation, splash screens, and operating-system contexts:
<link rel="manifest" href="/app.webmanifest">
{
"name": "Example app",
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}
Manifest icon src values resolve against the manifest URL. A manifest supplements the favicon; it does not replace the <link rel="icon"> used for ordinary page tabs. See MDN’s web app manifest documentation.
6. Verify the deployed icon
Check the exact URL in a browser first. Then inspect the response headers and status from a terminal:
curl -I https://example.com/favicon.ico
You want a successful status and an image content type such as image/x-icon or image/png. A redirect can work, but an HTML error page returned with status 200 is still a broken favicon.
Python check:
import requests
url = "https://example.com/favicon.ico"
r = requests.get(url, timeout=20)
r.raise_for_status()
print(r.headers.get("content-type"), len(r.content))
with open("favicon-check.ico", "wb") as f:
f.write(r.content)
Node.js check:
const res = await fetch('https://example.com/favicon.ico');
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
console.log(res.headers.get('content-type'), res.headers.get('content-length'));
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| No icon appears | The link is outside <head>, has the wrong rel, or the URL is wrong. |
Move it into <head>, use rel="icon", and open href directly. |
| 404 in DevTools | The asset was not copied to the deployed public directory. | Inspect the build output and deploy the file at the exact path. |
| Works locally, fails in production | Case mismatch, base path, CDN routing, or a missing static-file rule. | Check the production URL, filename case, and server static-file configuration. |
| HTML is returned instead of an image | A fallback route serves the app shell for unknown files. | Configure the favicon as a real static asset and verify its content type. |
| Old icon persists | Browsers and CDNs cache favicons aggressively. | Hard refresh, test in a private window, purge the CDN, or temporarily append a version query such as /favicon.png?v=2. |
| PWA icon is missing | The manifest path, JSON, icon dimensions, or HTTPS setup is invalid. | Validate the manifest separately; do not use it as a substitute for the favicon link. |
8. Performance, reliability, and security notes
- Keep favicon files small. They are requested early and may be fetched on many navigations.
- Serve them over HTTPS with the same origin when practical.
- Set a long cache lifetime for immutable, versioned files. Change the URL when replacing the artwork.
- Use a real image content type and avoid routing the request through application code.
- Do not put secrets or user-specific data in an icon URL; favicon requests can be made by browsers and crawlers.
- For multiple sizes, publish only the sizes you need. Extra links increase selection complexity without guaranteeing a visible difference.
Or skip the browser setup
If you need to inspect how a deployed page renders, ScreenshotNeo can capture the page after the browser loads it. Its API can show the favicon in the resulting screenshot while handling browser setup for you. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server lets AI agents such as Claude or Cursor take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
Is favicon.ico required?
No. PNG, GIF, and commonly SVG files can be referenced with rel="icon". ICO remains a practical fallback.
Does the favicon link belong in every HTML file?
It must be present in the rendered document head. A shared layout or root template usually adds it to every page.
Why does the tab show the old icon after deployment?
Favicon caching is common. Test the direct URL, use a new versioned filename or query string, and purge any CDN cache.
Is a manifest enough for a normal website?
No. Keep the ordinary favicon link for tabs and bookmarks. Add a manifest when you also support PWA installation.


