Favicon ToolsVérifierCréer
DevEx
Governance

A Favicon for Every Subdomain: The Full Playbook

· 7 min read

The docs favicon is the first domino. Once you accept that a subdomain is a distinct surface and not just another page, the same logic applies to your status page, your blog, your admin console, and your API explorer. Here's how to give each one its own tab identity from a single shared mark — without turning your brand into a ransom note.

One mark, a palette of frames

The trick that makes a per-subdomain system work is the same one that makes the docs variant work: don't touch the logo, change the frame. Keep the identical interior mark on every surface and vary a coloured outline around it. The brand stays intact everywhere; the ring colour becomes a legend the eye learns in a day.

Pick the colours from a shared palette — ideally your existing design tokens — so the frames feel like part of the brand rather than stickers slapped on top. The app keeps the clean, unframed mark as the canonical baseline; everything else is a recognisable variation of it.

A worked subdomain legend

SubdomainFrameReasoning
app.yoursite.comNone (clean mark)The reference icon. Keep it unframed so the customer-facing app is instantly the "real" one.
docs.yoursite.comAccent or contrast outlineThe highest-traffic pairing with the app; make this one unmistakable.
status.yoursite.comAmber outlineColour it like a warning light — you open it when something's wrong and want it fast.
admin.yoursite.comRed outlineA standing reminder that this tab has elevated privileges. Never confuse it with the app.
blog / wwwNeutral / white outlineLow-stakes, but worth marking so a stale CMS default doesn't sit next to your polished app.

Keep it legible, keep it short

Two rules keep the system from becoming noise. First, everything reads at 16 pixels: an outline works, a tiny wordmark or a four-character badge does not. Second, cap the palette. Four or five frame colours is a legend people can hold in their head; a dozen is just a different kind of confusion. If you run more subdomains than that, group them — all internal tools share one frame, all public content shares another.

Rolling it out

  1. 1

    List your hosts and rank them

    Write down every subdomain a user or teammate keeps open. Rank by how often it sits next to the app in a tab bar — that's your priority order. Docs is usually first.

  2. 2

    Assign frames from your tokens

    Map each host to a frame colour drawn from your existing palette. Leave the app unframed as the baseline. Write the legend down next to your brand guidelines.

  3. 3

    Generate each set from the one mark

    Produce a prefixed icon set per surface (docs-, status-, admin-) from the same source logo with the outline applied. Every set includes the full favicon.ico + PNG sizes + apple-touch-icon + manifest.

  4. 4

    Serve by host

    If each subdomain is its own deploy, the prefixed set is just its normal favicon. If several share one app, pick the prefix from the request host on the server so the right icon ships in the initial HTML.

  5. 5

    Verify the deployed bytes

    Add a check that downloads each host's favicon in CI and confirms it's the expected set, so a refactor can't silently reset a subdomain to the app icon.

Switching the icon set by hostname

// Next.js: map the request host to an icon prefix and manifest.
import { headers } from "next/headers";

const SURFACE_BY_SUBDOMAIN: Record<string, string> = {
  docs: "/docs-",
  status: "/status-",
  admin: "/admin-",
};

export default async function RootLayout({ children }) {
  const host = (await headers()).get("host") ?? "";
  const sub = host.split(".")[0];
  const prefix = SURFACE_BY_SUBDOMAIN[sub] ?? "/"; // app is the clean baseline
  const manifest = prefix === "/" ? "/manifest.json" : `${prefix}manifest.json`;

  return (
    <html>
      <head>
        <link rel="icon" href={`${prefix}favicon.ico`} />
        <link rel="icon" type="image/png" sizes="32x32" href={`${prefix}favicon-32x32.png`} />
        <link rel="icon" type="image/png" sizes="16x16" href={`${prefix}favicon-16x16.png`} />
        <link rel="apple-touch-icon" href={`${prefix}apple-touch-icon.png`} />
        <link rel="manifest" href={manifest} />
      </head>
      <body>{children}</body>
    </html>
  );
}

One table, one lookup. Adding a subdomain is a single new entry, and unknown hosts fall back to the clean app icon.

Per-subdomain favicon FAQ

Won't a dozen frame colours look chaotic?

Yes — which is why you cap the palette. Four or five colours is a legend people learn quickly. Beyond that, group surfaces (all internal tools one colour, all public content another) rather than inventing a new frame per host.

Do search engines get confused by different favicons per subdomain?

No. Each host is its own site to a crawler, and each can legitimately have its own favicon. Google shows a per-site icon in results, so a distinct docs or status icon is expected, not penalised.

What about apps on a path instead of a subdomain?

The same idea works — switch the prefix on the pathname instead of the host (e.g. /docs/* gets the docs set). Subdomains are just the cleanest split because the browser treats them as separate origins.

Generate your icon set

Keep reading