Based on Vemetric/favicon-api — a TypeScript/Bun favicon API service.
favifetchis the Go library version of the same concept.
A Go library for discovering and fetching website favicons. Automatically finds the best favicon from HTML <link> tags, web app manifests, common fallback locations, and optionally Vemetric's favicon API.
- Smart Discovery — Finds favicons from
<link rel="icon">,<link rel="apple-touch-icon">,manifest.json, and common fallback paths (/favicon.ico,/apple-touch-icon.png) - Quality Ranking — Scores favicons by size, format, and source, returning the best one first
- Format Detection — Detects PNG, JPEG, GIF, ICO, WebP, SVG, BMP via magic bytes
- Image Processing — Resize to any pixel size and convert between PNG, JPG, WebP
- SSRF Protection — Blocks requests to private IPs by default
- Domain Mappings — Maps app package names (e.g.
com.pinterest) to canonical domains - Vemetric API Fallback — Optionally uses Vemetric's
favicon.vemetric.comas a last-resort source - Browser Mode — Selects the regular favicon Chromium would use for a 16px desktop tab from the initial HTML
- Context Support — Full
context.Contextintegration for cancellation and timeouts - Zero cgo — Pure Go image processing (no Sharp/Node dependency)
go get github.com/kodehat/favifetchpackage main
import (
"context"
"fmt"
"os"
"github.com/kodehat/favifetch"
)
func main() {
ctx := context.Background()
// Fetch a favicon
result, err := favifetch.Fetch(ctx, "github.com")
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Format: %s\n", result.Format) // png, svg, etc. (implements Stringer)
fmt.Printf("Size: %dx%d (%d bytes)\n", result.Width, result.Height, result.Size)
fmt.Printf("Source: %s\n", result.Source)
fmt.Printf("URL: %s\n", result.SourceURL)
// result.Data contains the raw image bytes
// os.WriteFile("favicon."+result.Format, result.Data, 0644)
}result, err := favifetch.Fetch(ctx, "github.com")
// result.Data → raw image bytes
// result.Format → "svg", "png", "ico", etc.URLs without a scheme default to https://. Full URLs with paths are also supported:
result, err := favifetch.Fetch(ctx, "https://example.com/blog/post")Resize to 128×128 and convert to PNG:
result, err := favifetch.Fetch(ctx, "github.com",
favifetch.WithSize(128),
favifetch.WithFormat(favifetch.TargetPNG),
)Custom timeout and user agent:
result, err := favifetch.Fetch(ctx, "example.com",
favifetch.WithTimeout(10*time.Second),
favifetch.WithUserAgent("MyApp/2.0"),
)Disable Vemetric API fallback and SSRF protection:
result, err := favifetch.Fetch(ctx, "internal.company.com",
favifetch.WithFallbackAPI(false),
favifetch.WithBlockPrivateIPs(false),
)Use a self-hosted Vemetric-compatible API (HTTPS is used; a port is optional):
result, err := favifetch.Fetch(ctx, "example.com",
favifetch.WithVemetricAPIHost("favicons.example.com:8443"),
)Use browser mode when the selected favicon source should follow Chromium-style regular tab-icon discovery instead of favifetch's quality ranking:
result, err := favifetch.Fetch(ctx, "example.com",
favifetch.WithMode(favifetch.ModeBrowser),
)Browser mode considers only HTML <link rel="icon"> candidates and falls back
to /favicon.ico only when the page declares none. When WithFallbackAPI(true)
(the default) is enabled, it uses the Vemetric API only after those candidates
fail, or immediately when the initial HTML request fails (for example, a 403).
It honors the final page URL and <base href>, returns the original downloaded
bytes, and cannot be combined with WithSize or WithFormat. It does not
execute JavaScript or reproduce a browser session's cookies and other runtime
state. It always uses a Chromium-like User-Agent; WithUserAgent is ignored in
this mode.
List all discovered favicon sources without fetching any image:
sources, err := favifetch.Discover(ctx, "github.com")
for _, s := range sources {
fmt.Printf("[%s] score=%d size=%d %s\n", s.Source, s.Score, s.Size, s.URL)
}
// Output:
// [link-tag] score=150 size=0 https://github.githubassets.com/favicons/favicon.svg
// [link-tag] score=70 size=32 https://github.githubassets.com/favicons/favicon.png
// [manifest] score=40 size=192 https://github.githubassets.com/assets/icon-192.png
// [fallback] score=20 https://github.com/apple-touch-icon.png
// [fallback] score=10 https://github.com/favicon.ico
// [fallback-api] score=1 https://favicon.vemetric.com/github.com?size=64Discovers and fetches the best favicon for the given URL.
Returns all discovered favicon sources without fetching any image.
type FaviconResult struct {
Data []byte // Raw image bytes
Format DetectedFormat // png, jpg, svg, ico, webp, gif, bmp
Width int // Image width in pixels
Height int // Image height in pixels
Source string // "link-tag", "manifest", "fallback", "fallback-api"
SourceURL string // Original URL the favicon was fetched from
Size int // Size of Data in bytes
}
type DiscoveredSource struct {
URL string
Size int
Format DetectedFormat
Source string
Score int
}| Option | Default | Description |
|---|---|---|
WithUserAgent(s) |
"Favifetch/1.0" |
User-Agent header |
WithTimeout(d) |
5s |
Request timeout |
WithMaxImageSize(n) |
5MB |
Max favicon size to accept |
WithMaxRedirects(n) |
5 |
Max HTTP redirects |
WithBlockPrivateIPs(bool) |
true |
Block private IP ranges |
WithFallbackAPI(bool) |
true |
Use Vemetric favicon API fallback |
WithVemetricAPIHost(host) |
favicon.vemetric.com |
Set Vemetric-compatible API host (HTTPS) |
WithSize(px) |
0 |
Resize to px×px (0 = no resize) |
WithFormat(f) |
TargetUnspecified |
Convert to TargetPNG, TargetJPEG, or TargetWebP |
WithHTTPClient(c) |
http.DefaultClient |
Custom HTTP client |
WithPreferredFormats(f...) |
(default order) | Set preferred favicon formats in priority order |
The library searches these sources in priority order:
- HTML
<link>tags —<link rel="icon">,<link rel="apple-touch-icon">,<link rel="mask-icon"> - Web App Manifest —
manifest.jsonicons array - Common fallbacks —
/apple-touch-icon.png(score 20),/favicon.ico(score 10) - Vemetric API —
favicon.vemetric.com/<domain>?size=<n>(score 1, optional)
Scoring weights SVG (+100), large sizes (+90 for ≥512px), PNG (+20), WebP (+15), and apple-touch-icon (+10). Mask icons are penalized (−10).
You can customize format priorities with WithPreferredFormats (see below).
The WithPreferredFormats option lets you control which favicon formats are preferred:
// Prefer SVG, fall back to PNG, then ICO
result, err := favifetch.Fetch(ctx, "example.com",
favifetch.WithPreferredFormats(favifetch.FormatSVG, favifetch.FormatPNG, favifetch.FormatICO),
)When preferences are set, sources matching a higher-ranked format get a large score bonus, ensuring they are tried first. Unlisted formats are still tried as a last resort if no preferred format succeeds. Within the same format tier, larger sizes and better sources still win.
// Prefer PNG only — skip SVG entirely unless nothing else works
result, err := favifetch.Fetch(ctx, "example.com",
favifetch.WithPreferredFormats(favifetch.FormatPNG),
)When no preferences are set (or nil), the default order is: SVG > PNG > WebP > JPEG > ICO > GIF > BMP.
favifetch.ErrPrivateIP— returned when the target URL resolves to a private IP andBlockPrivateIPsis enabledfavifetch.errInvalidURL— returned for malformed URLs or unsupported schemes
Based on Vemetric/favicon-api, a TypeScript/Bun favicon API service by Vemetric.
Built with:
golang.org/x/net/html— HTML parsinggolang.org/x/image— Image resamplinggithub.com/HugoSmits86/nativewebp— WebP encode/decodegithub.com/srwiley/oksvg+github.com/srwiley/rasterx— SVG rasterization
MIT