You replace the logo, deploy the site, and still see the old favicon in a browser tab.
You regenerate the file. The old icon remains. Then someone installs the site on their phone and gets a different icon again.
The problem may have nothing to do with the logo itself. A site can expose multiple icon files, multiple declarations, cached responses, and a manifest used by installation surfaces.
A favicon is a small asset with a surprisingly complete delivery path: design, encoding, HTML discovery, fetching, caching, and display.
Here is a framework-neutral workflow for treating it like a real deployment.
1. Design for the smallest use first
A wordmark that looks excellent at 800 pixels wide may become unreadable in a 16-pixel tab icon.
Do not start by generating every output size from the full logo and assume the design problem is solved.
Choose a recognizable mark: a symbol, a simplified initial, or another compact part of the identity. Look at it at actual small sizes. Thin strokes, tiny gaps, and decorative details can disappear.
My suggested acceptance questions are:
- Is the mark recognizable at 16 and 32 pixels?
- Does it have enough internal space after resizing?
- Is it distinguishable on light and dark surrounding UI?
- Is it clearly related to the product's identity?
A larger source improves the available input quality. It does not guarantee that every small derivative has a useful silhouette.
For a new product, review the tiny icon before committing to a final export package. It is easier to simplify the source once than to debug ten blurry outputs later.
2. Separate icon purposes
Different browser and platform surfaces can use different declarations.
| Artifact | Intended role in this reference package |
|---|---|
favicon.ico |
Conventional browser icon fallback with multiple sizes |
| Small PNG icons | Explicit raster tab-icon options |
apple-touch-icon.png |
Apple touch-icon declaration |
| Larger manifest icons | App-installation surfaces that use the manifest |
site.webmanifest |
Metadata referencing the manifest icon files |
An SVG tab icon can be another option when it fits the target browser support and your artwork. It does not eliminate the need to verify other surfaces.
The HTML link element supplies resource relationships. Its sizes and type metadata help describe icon candidates; listing a size does not resize an incorrectly generated file.
For app icons, the manifest icons reference describes the distinct metadata used there.
3. Use explicit paths and real dimensions
Here is a reference HTML declaration set. Adjust the paths to the files you actually ship:
<link rel="icon" href="/icons/favicon.ico" type="image/x-icon">
<link rel="icon" href="/icons/favicon-32x32.png"
type="image/png" sizes="32x32">
<link rel="icon" href="/icons/favicon-16x16.png"
type="image/png" sizes="16x16">
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png"
sizes="180x180">
<link rel="manifest" href="/site.webmanifest">
This is a template, not a universal platform checklist. Frameworks may generate their own declarations from filesystem conventions or metadata APIs.
Inspect the resulting HTML rather than adding duplicate tags blindly. An inherited layout, a CMS theme, and a plugin can each contribute icon declarations.
If the application is served below a path such as /portal/, a root-relative /icons/ URL points to the origin root. Decide whether that is where your assets live. Do not assume your application base path is automatically prefixed.
The MDN rel reference is useful when checking the relationships you declare.
4. Treat the manifest as another linked document
A minimal example might contain:
{
"name": "Example Workspace",
"short_name": "Workspace",
"start_url": "/",
"display": "standalone",
"icons": [
{
"src": "/icons/app-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/app-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
}
]
}
Those filenames are illustrative. Verify they exist and have the declared dimensions.
A manifest is not an installability guarantee. Requirements and behavior depend on the platform and the rest of the application.
Also check where relative paths resolve. A relative icon path is resolved from the manifest URL, not simply from whichever application screen you happened to be viewing.
For a subpath deployment, inspect start_url, scope, and icon locations together. A valid JSON file can still point users or fetches to the wrong place.
5. Do not label an ordinary icon maskable without checking it
Some app surfaces apply shape masks. An icon designed to reach the corners of its square may lose important content when cropped.
A purpose value of maskable is a promise about how the artwork should survive those treatments. It is not a request for the browser to repair the design.
If you need maskable support, create and test artwork for it, and declare that specific file appropriately. Keep critical content within the required safe region and check previews using the platform's tooling.
Do not claim a package provides maskable artwork merely because it includes a 512-pixel PNG. Size and maskability are different properties.
This is particularly relevant for logos whose essential letter or symbol sits near an edge. Test the silhouette after the mask, not only before it.
6. Debug the fetched asset before blaming the cache
A filename ending in .png can still return an HTML error page. A single-page application may answer an unknown asset route with its application shell.
For each declared icon, verify:
- The requested URL is correct.
- The response succeeds.
- The response content type matches an actual image.
- The decoded dimensions match the declaration.
- The response body is the new artwork.
A 200 status alone is insufficient.
In a browser console, this helper checks a raster asset's actual decoded dimensions:
async function inspectRasterIcon(url) {
const response = await fetch(url, { cache: "no-store" });
if (!response.ok) {
throw new Error(`Icon request failed: ${response.status}`);
}
const contentType = response.headers.get("content-type") || "";
if (!contentType.toLowerCase().startsWith("image/")) {
throw new Error(`Expected image response, got ${contentType}`);
}
const bitmap = await createImageBitmap(await response.blob());
try {
return { width: bitmap.width, height: bitmap.height, contentType };
} finally {
bitmap.close();
}
}
Use it for a same-origin PNG or another supported raster input. It is not an ICO-frame inspector, an SVG renderer, or a guarantee that browser tab selection uses that particular candidate. Cross-origin requests also require appropriate CORS access.
7. Make updates observable
If the server delivers the new bytes but the tab still shows the old icon, separate the possible layers: HTTP cache, service-worker cache, browser icon storage, an already-installed app icon, and stale HTML declarations.
A versioned asset name such as favicon-32x32-v2.png can make an updated candidate easier to identify. Update the declaration and ship the new file together.
Versioning is not permission to delete every previous asset immediately. Cached HTML or an older app installation may still refer to it.
For a simple release, I would:
- Deploy new versioned icon files.
- Update HTML and manifest references.
- Check the served page and manifest.
- Inspect service-worker behavior if present.
- Test a fresh browser context and a new installation separately.
Avoid treating one hard reload as proof that every installation surface has updated. An icon attached to a saved shortcut can have its own lifecycle.
8. Keep a small release checklist
| Check | Evidence |
|---|---|
| Small-size readability | Actual-size 16 and 32 pixel previews |
| HTML declarations | Rendered document points to intended files |
| Asset delivery | Correct status, type, bytes, and dimensions |
| Manifest | Valid JSON with working icon URLs |
| Deployment paths | Correct under the production base path |
| Light and dark UI | Recognizable mark on target surfaces |
| Installation | Real device or supported platform test |
| Update | New references visible after deployment |
This is a narrow icon-delivery checklist. It does not replace testing the rest of a PWA or auditing the security of SVG input in a generator.
The aim is to leave no mystery about which file was generated, which file was fetched, and which surface was inspected.
Generate the package, then verify the deployment
BatchSet's Favicon Generator generates an ICO, PNG sizes, an Apple touch icon, larger app icons, and a web manifest locally in your browser.
Use a simple square brand mark, download the package, and adapt the provided declarations to your actual deployment paths. Treat generated files as a starting package, not proof that every browser or installation surface is already configured.
A tiny icon deserves a small, deliberate deployment check. That is usually faster than repeatedly regenerating the logo while the wrong file keeps being served.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.