Problem#
Adding a custom favicon to a Hugo site is straightforward — drop a favicon.ico into static/. But making it appear circular in the browser tab is trickier than it sounds.
ImageMagick’s alpha channel handling for ICO files is unreliable. The corners either don’t go transparent or the 1-bit alpha makes edges look jagged. The browser ends up showing a square icon regardless.
Solution#
Use Python’s Pillow library to apply an ellipse mask with proper RGBA transparency, then serve the result as a PNG via a custom favicons.html partial.
1. Set up a virtual environment and install Pillow#
python3 -m venv .venv
source .venv/bin/activate
pip install Pillow2. Generate the circular favicon#
from PIL import Image, ImageDraw
src = "static/images/your-image.jpg"
out_dir = "static"
img = Image.open(src).convert("RGBA")
w, h = img.size
# Crop a centered square
size = min(w, h)
left = (w - size) // 2
top = (h - size) // 2
img = img.crop((left, top, left + size, top + size))
def make_circle(img, out_size):
img = img.resize((out_size, out_size), Image.LANCZOS)
mask = Image.new("L", (out_size, out_size), 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0, out_size, out_size), fill=255)
result = Image.new("RGBA", (out_size, out_size), (0, 0, 0, 0))
result.paste(img, mask=mask)
return result
for size, name in [(64, "favicon.png"), (32, "favicon-32x32.png"), (16, "favicon-16x16.png")]:
make_circle(img, size).save(f"{out_dir}/{name}", "PNG")This produces PNG files with true RGBA transparency — pixels outside the circle are fully transparent.
3. Override the theme’s favicon partial#
Create layouts/partials/favicons.html so Hugo uses the PNG instead of falling back to favicon.ico:
<link rel="icon" type="image/png" href="{{ "favicon.png" | relURL }}">The blowfish theme (and most Hugo themes) check for this partial first:
{{ if templates.Exists "partials/favicons.html" }}
{{ partialCached "favicons.html" .Site }}
{{ else }}
...fallback to favicon-32x32.png / favicon.ico...
{{ end }}4. Rebuild and hard-refresh#
hugo server -DOpen the site and press Cmd+Shift+R to bypass the browser’s favicon cache. The icon will appear circular — transparent corners blend with the browser tab background.
Why not ImageMagick?#
ImageMagick can apply circular masks, but when writing to ICO format it downgrades to 1-bit alpha (binary transparency, no anti-aliasing). The result looks jagged. PNG with full 8-bit alpha via Pillow solves this cleanly.