Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To fetch a website favicon reliably, request the page’s HTML, find its declared icon links, resolve each icon URL against the page URL, and fetch the best usable candidate. If the page declares no working icon, try the conventional /favicon.ico path at the site’s origin. A browser-based cross-origin request may be blocked by CORS; when your application needs to inspect the HTML or image bytes, do the fetching on a server you control.

How favicon discovery works

A favicon is not necessarily a file named favicon.ico in a website’s root directory. The page can declare one or more icons in its HTML head using <link> elements. The href may be a relative path, an absolute URL, or a URL on a CDN. Google’s example uses <link rel="icon" href="/path/to/favicon.ico">, and its documentation allows relative and absolute URLs: Google Search Central’s favicon guidance.

For a general-purpose resolver, use this order:

  1. Validate and normalize the input as an HTTP or HTTPS URL.
  2. Fetch the page HTML with limits on time, redirects, and response size.
  3. Find icon links by examining their space-separated rel tokens.
  4. Resolve each non-empty href against the page URL.
  5. Filter and rank candidates using supported formats, sizes, and applicable media.
  6. Fetch candidates in ranked order; accept one only if the response and image data are usable.
  7. If none works, try /favicon.ico at the page’s origin. If that fails, return a documented “not found” result.

The HTML Standard says a user agent may resolve /favicon.ico against an HTTP or HTTPS document URL when no icon link is declared; this makes it a useful fallback, not a guarantee that every site has an icon there. See the WHATWG HTML Standard. MDN describes rel="icon" and the hints browsers can use when several icon links are present: MDN’s rel reference.

Fetch a favicon with Python

This example runs on a trusted machine or service and returns the first valid raster icon it can fetch. It checks declared candidates before falling back to the root icon, follows no more than five redirects, applies request timeouts, caps downloaded bodies, and verifies raster image bytes with Pillow. It deliberately does not treat an SVG response as a valid raster image; if your consumer supports SVG, implement a separate, explicit SVG validation and output path.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Elebase USB to USB C Adapter for iPhone 18 Pro Max,USBC Car Charger Adapter
  • Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
  • Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
  • Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
  • Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
  • 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.

Install the dependencies with python -m pip install requests beautifulsoup4 pillow, save the following as fetch_favicon.py, and run python fetch_favicon.py https://example.com.

import sys
from urllib.parse import urljoin, urlparse

import requests
from bs4 import BeautifulSoup
from PIL import Image, UnidentifiedImageError
from io import BytesIO

MAX_HTML_BYTES = 2_000_000
MAX_IMAGE_BYTES = 5_000_000
TIMEOUT = (5, 15)  # connect timeout, read timeout
MAX_REDIRECTS = 5
RASTER_TYPES = {
    "image/png", "image/jpeg", "image/gif", "image/webp", "image/x-icon", "image/vnd.microsoft.icon"
}
ICON_RELS = {"icon", "shortcut", "apple-touch-icon", "apple-touch-icon-precomposed"}


def checked_http_url(value):
    parsed = urlparse(value)
    if parsed.scheme.lower() not in {"http", "https"} or not parsed.hostname:
        raise ValueError("Enter a valid HTTP or HTTPS URL")
    return value


def get_limited(session, url, limit, accepted_types=None):
    checked_http_url(url)
    response = session.get(
        url, timeout=TIMEOUT, allow_redirects=True, stream=True
    )
    if len(response.history) > MAX_REDIRECTS:
        response.close()
        raise ValueError("Too many redirects")
    response.raise_for_status()
    content_type = response.headers.get("Content-Type", "").split(";", 1)[0].strip().lower()
    if accepted_types is not None and content_type not in accepted_types:
        response.close()
        raise ValueError("Unexpected content type: " + (content_type or "missing"))
    chunks, total = [], 0
    for chunk in response.iter_content(65536):
        if not chunk:
            continue
        total += len(chunk)
        if total > limit:
            response.close()
            raise ValueError("Response exceeds the configured byte limit")
        chunks.append(chunk)
    final_url = response.url
    response.close()
    return b"".join(chunks), content_type, final_url


def size_score(sizes, requested=32):
    # Prefer a declared size at least as large as requested; otherwise use the largest.
    values = []
    for item in (sizes or "").lower().split():
        if item == "any":
            continue
        try:
            width, height = (int(part) for part in item.split("x", 1))
            if width > 0 and height > 0:
                values.append(min(width, height))
        except (ValueError, TypeError):
            pass
    if not values:
        return (1, 0)  # unknown sizes rank below known adequate sizes
    adequate = [n for n in values if n >= requested]
    return (3, -min(adequate)) if adequate else (2, max(values))


def media_matches(media):
    # This small example handles common unconditional and screen hints.
    # A production implementation should evaluate media queries for its target.
    value = (media or "").strip().lower()
    return not value or value == "all" or value == "screen"


def image_is_decodable(data):
    try:
        with Image.open(BytesIO(data)) as image:
            image.verify()
        return True
    except (UnidentifiedImageError, OSError, ValueError):
        return False


def fetch_favicon(page_url, requested_size=32):
    page_url = checked_http_url(page_url)
    session = requests.Session()
    session.max_redirects = MAX_REDIRECTS
    html, _, final_page_url = get_limited(
        session, page_url, MAX_HTML_BYTES, {"text/html", "application/xhtml+xml"}
    )
    soup = BeautifulSoup(html, "html.parser")
    candidates = []
    for link in soup.find_all("link"):
        rel = link.get("rel", [])
        if isinstance(rel, str):
            rel = rel.split()
        if not ICON_RELS.intersection(token.lower() for token in rel):
            continue
        href = (link.get("href") or "").strip()
        if not href or not media_matches(link.get("media")):
            continue
        icon_url = urljoin(final_page_url, href)
        try:
            checked_http_url(icon_url)
        except ValueError:
            continue
        declared_type = (link.get("type") or "").split(";", 1)[0].strip().lower()
        # A missing type is allowed; an explicitly unsupported type is skipped.
        if declared_type and declared_type not in RASTER_TYPES:
            continue
        candidates.append((size_score(link.get("sizes"), requested_size), icon_url))

    candidates.sort(key=lambda item: item[0], reverse=True)
    tried = set()
    for _, icon_url in candidates:
        if icon_url in tried:
            continue
        tried.add(icon_url)
        try:
            data, mime, final_url = get_limited(session, icon_url, MAX_IMAGE_BYTES, RASTER_TYPES)
            if image_is_decodable(data):
                return final_url, mime, data
        except (requests.RequestException, ValueError):
            continue

    fallback = urljoin(final_page_url, "/favicon.ico")
    try:
        data, mime, final_url = get_limited(session, fallback, MAX_IMAGE_BYTES, RASTER_TYPES)
        if image_is_decodable(data):
            return final_url, mime, data
    except (requests.RequestException, ValueError):
        pass
    return None


if __name__ == "__main__":
    if len(sys.argv) != 2:
        raise SystemExit("Usage: python fetch_favicon.py https://example.com")
    result = fetch_favicon(sys.argv[1])
    if result is None:
        raise SystemExit("No usable favicon found")
    url, mime, data = result
    with open("favicon.bin", "wb") as output:
        output.write(data)
    print(f"Fetched {url} ({mime}, {len(data)} bytes) to favicon.bin")

The script uses a simple ranking policy: known sizes at least as large as the requested 32-pixel display size rank first, then smaller declared sizes, then unknown sizes. It does not claim that this is the only correct policy. MDN identifies media, type, and sizes as selection hints, but the exact ranking policy depends on your application and the icon consumer.

Rank #2
Anker USB-C Hub, 5-in-1 USB Hub for Laptops, 4K HDMI Multiport Adapter
  • 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
  • 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
  • Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
  • 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
  • What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.

Resolve URLs and select candidates carefully

Read rel as tokens

Do not test whether the whole rel attribute equals a single string. HTML allows multiple space-separated relationship tokens. A resolver can consider icon, shortcut icon (the two tokens shortcut and icon), apple-touch-icon, and apple-touch-icon-precomposed. Ignore empty or missing href values rather than turning them into a request to the page itself.

Resolve relative paths against the page

Given a page at https://example.com/blog/post, an icon /icons/site.png resolves from the origin root, while icons/site.png resolves relative to the page path. Use a URL resolver such as Python’s urljoin instead of joining strings by hand. An absolute CDN URL should remain on that CDN host; do not assume an icon is served by the same host as the HTML.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
Sale
Anker USB C Hub, 7in1 Multi-Port USB Adapter, 4K@60Hz USBC to HDMI Splitter
  • Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
  • Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
  • Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
  • Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
  • What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.

Use hints without assuming they are perfect

sizes can advertise dimensions or the keyword any; type can identify a declared format; and media can make a link applicable only in a particular context. Filter out media conditions your implementation does not support, and define how it handles missing hints. A declaration is a candidate, not proof that the resource exists, matches its declared type, or can be decoded.

Choose the output format for the downstream use. Keep a valid original file when fidelity or animation matters. If a consumer only accepts raster images, explicitly rasterize supported SVGs using a suitable, secure image pipeline rather than passing SVG through as though it were PNG. The Python example above accepts common raster media types and confirms decodability.

Rank #4
Sale
UGREEN USB to USB C Adapter Combo 4-Pack, 10Gbps USB C Converter Space Gray
  • Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
  • Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
  • Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
  • Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
  • Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Browser versus server-side fetching

A browser page making a cross-origin fetch() needs the target server to permit access with CORS response headers. Without them, your JavaScript cannot read the HTML response or inspect image bytes. MDN notes that no-cors can produce an opaque response, which is not a workaround when your code needs to examine the response: MDN Fetch metadata.

Approach Advantages Trade-offs
Browser fetch to another origin Runs near the user and avoids sending the target URL through your own backend. Depends on the target’s CORS policy; opaque responses cannot be parsed or validated by page JavaScript.
Your server or a controlled same-origin proxy Can read page markup and inspect downloaded image bytes without relying on browser access to a third-party response. Adds a network hop and server work; accepting arbitrary URLs creates server-side request forgery (SSRF) risk.

If the user only needs to display a known icon URL, a browser may be able to render it as an image even when JavaScript cannot inspect its bytes; that is different from discovering the icon by parsing another site’s HTML. Choose the architecture based on whether you need the image to display or need to read, validate, transform, or return its data.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Anker USB C Hub, 5-in-1 USBC to HDMI Splitter with 4K Display
  • 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
  • Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
  • Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
  • HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
  • What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.

Protect a server-side resolver

A URL submitted by a user is untrusted input. The Python example demonstrates basic limits, but it is not a complete SSRF defense and should not be exposed as-is as a public URL-fetching service. In production, apply controls before the first request and after every redirect:

  • Allow only HTTP and HTTPS; reject malformed URLs, credentials in URLs, and unexpected ports if your product does not need them.
  • Resolve DNS and reject loopback, private, link-local, multicast, and reserved IP addresses. Re-check the resolved destination at connection time to reduce DNS-rebinding risk.
  • Validate every redirect destination independently; do not let an allowed public URL redirect the service to an internal address.
  • Bound redirect count, connect and read timeouts, total elapsed time, response bytes, and concurrent fetches.
  • Do not trust Content-Length alone; enforce byte limits while streaming the response.
  • Validate HTTP status, media type, and actual decoded image bytes. Return a clear null/not-found result if declared candidates and the fallback fail.
  • Cache successful results for a documented duration if repeat requests are common, and avoid caching failures as permanent unless that behavior is intentional.

There is no universal timeout, redirect count, cache lifetime, or ranking rule for every application. Choose limits according to your service’s latency and security requirements and make them explicit.

Common failures and fixes

  • The browser reports a CORS error: the target has not authorized your page to read the response. Move discovery to your server or a controlled same-origin proxy; no-cors will not make the response inspectable.
  • The resolver returns the wrong icon: it may be picking the first link instead of evaluating all candidates. Resolve every usable link, account for type, size, and media hints, and define a deterministic ranking policy.
  • A relative icon URL fails: the raw href was requested directly. Resolve it against the final page URL after redirects before fetching it.
  • The page has no favicon.ico: that path is a fallback convention, not the authoritative location. Check declared links first and return a clear no-icon result if all options fail.
  • The response says “image” but cannot be opened: a server may return an HTML error page, an empty body, or malformed image data with an incorrect content type. Check status and MIME type, then validate the bytes with an image decoder.
  • An SVG icon is rejected: the example accepts raster formats only. Add explicit SVG support only if your consumer needs it, and treat SVG parsing or rasterization as a separate validation path.
  • Requests to arbitrary hosts expose internal services: the fetcher has an SSRF vulnerability. Restrict destinations and validate each redirect as described above; HTTP scheme validation by itself is insufficient.

Or skip the browser setup

ScreenshotNeo is a website screenshot API, not a favicon-discovery endpoint: use the resolver above when you need the icon file itself. If you also need a clean screenshot of a page, one GET request can return an image or PDF. For example:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation. ScreenshotNeo accepts cookie or consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. The other listed plans are Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000, and Business at $249 for 1,000,000; yearly billing gives two months free. Every feature is available on every plan. Visit ScreenshotNeo for the product, or sign up free for 1,000 screenshots a month with no card.

What a favicon fetch does not guarantee

Finding and downloading an icon is separate from how a search engine uses it. Google states that “A favicon isn’t guaranteed to appear in Google Search results, even if all guidelines are met.” Follow its favicon guidance, but do not treat a successful fetch as confirmation that a search result will display the icon.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.