A small, self-hosted Node.js API that discovers real website icons, rejects fake placeholders, and falls back to public icon sources when direct requests fail.
<img src="https://boluo66.top/favimg/?url=github.com" alt="GitHub">- One request —
/?url=github.comreturns an image; bare domains automatically use HTTPS. - Resilient discovery — page icon metadata →
/favicon.ico→ public fallback sources. - Real-image validation — rejects HTML responses, 1×1 pixels, and known provider placeholders.
- SSRF protection — blocks private and reserved addresses before the initial request and every redirect hop.
- HTTP caching — in-memory TTL cache, bounded eviction, content-based ETags, and
304responses. - Zero dependencies — runs on Node.js built-ins with no install or build step.
On 2026-07-15, all 12 major Chinese sites and 36 international sites in our sample returned valid images from a mainland China server. The first 28 results all had unique content hashes, so they were not a shared default placeholder. Chinese sites usually work directly; international sites rely more heavily on fallback providers. A cold request for sites such as Google, YouTube, X, or Facebook can take about 10 seconds, while cached responses usually return in tens of milliseconds.
git clone https://github.com/VeteranBoLuo/favicon-api.git
cd favicon-api
npm startOpen http://localhost:3456 for the interactive playground, or request an icon directly:
curl "http://localhost:3456/?url=github.com" -o github.svgThe public usage total is kept in memory by default. Set a writable file path to persist it across restarts; FAVICON_USAGE_COUNT_START can seed an existing historical total:
FAVICON_USAGE_COUNT_FILE=./data/usage-count.json \
FAVICON_USAGE_COUNT_START=12000 npm startdocker build -t favicon-api .
docker run --rm -p 3456:3456 favicon-apihttps://boluo66.top/favimg/?url=github.com
https://boluo66.top/favimg/?url=https%3A%2F%2Fgithub.com%2Fopenai
Successful responses contain the image binary and these useful headers:
| Header | Description |
|---|---|
Content-Type |
Detected image type |
Cache-Control |
Browser and CDN cache policy |
ETag |
Content-based validator; supports If-None-Match |
X-Favicon-Source |
Final source URL after redirects or fallback |
Errors use JSON: { "error": "..." } with 400, 403, 404, or 502 status codes.
{ "status": "ok" }Returns the number of qualified, successful favicon fetches. Built-in playground previews are excluded.
{ "count": 12001 }The public total counts the same client and normalized hostname at most once every 30 minutes; different hostnames still count, so legitimate bulk imports remain represented. After 3,000 distinct hostnames from one client within 10 minutes, only the public counter pauses — API responses continue normally. Set FAVICON_TRUST_PROXY=1 only behind a proxy that overwrites X-Real-IP; FAVICON_USAGE_TRUSTED_IPS can exempt trusted first-party importers from the extreme burst threshold.
- Normalize the input URL and reject private or reserved destinations.
- Fetch the page with a short timeout and manually validate every redirect destination.
- Select the best icon declared by the page, otherwise try
/favicon.ico. - Validate the image signature and reject tiny or known placeholder images.
- Try public fallback sources when direct discovery fails, then cache the valid result.
This service fetches user-supplied public URLs. It blocks private, loopback, link-local, documentation, and other reserved address ranges; limits redirect hops and response sizes; and re-checks each redirect destination. DNS rebinding cannot be eliminated completely without pinning DNS resolution to the outbound connection, so use additional egress controls for high-trust production environments.
Fallback requests may send the requested hostname to favicone.com or Yandex. Remove or replace AGGREGATOR_BUILDERS in src/favicon.js if your environment must avoid third-party services.
npm test
npm run checkContributions and reproducible edge cases are welcome. See CONTRIBUTING.md.
MIT © VeteranBoLuo