# Hyperflow Favicon

Dynamically generates multi-resolution ICO favicons for Webflow sites by extracting and composing icon images from the site's homepage.&#x20;

{% hint style="warning" %}
**STATUS: TESTING** \
Currently in SEO testing on Sygnal's site.  &#x20;

<https://www.sygnal.com/favicon.ico>&#x20;
{% endhint %}

### Features&#x20;

#### Smart Icon Detection

* Automatically fetches the homepage and extracts icon links
* Supports multiple icon types:
  * `<link rel="shortcut icon">`
  * `<link rel="icon">`
  * `<link rel="apple-touch-icon">`
* Resolves relative and absolute URLs correctly

#### Multi-Format Support

* **Primary**: PNG images (most common in Webflow)
* **ICO extraction**: Can extract PNG data from existing ICO files
* **Graceful degradation**: Skips unsupported formats (GIF, JPG) that require transcoding

#### ICO Composition

* Builds valid multi-resolution ICO files from multiple PNG sources
* Embeds PNGs directly without transcoding (efficient and fast)
* Sorts icons by size for optimal browser compatibility
* Supports icons up to 256×256 pixels

#### Performance & Caching

* **Edge caching**: Returns cached ICO files instantly for repeat requests
* **7-day cache**: `Cache-Control: public, max-age=604800, immutable`
* **Upstream caching**: Caches homepage and icon fetches at Cloudflare edge
* Zero dependencies, optimized for Cloudflare Workers runtime

#### Error Handling

* Gracefully handles missing or invalid icons
* Continues processing if individual icon fetches fail
* Returns appropriate HTTP status codes (404, 502, 500)

### How It Works

1. **Request**: Browser requests `/favicon.ico` from your domain
2. **Fetch**: Worker fetches the homepage (`/`) from the same origin
3. **Parse**: Extracts icon URLs from `<link>` tags in the HTML
4. **Download**: Fetches each icon image (with edge caching)
5. **Process**: Reads PNG dimensions and validates format
6. **Compose**: Builds a multi-resolution ICO file containing all valid PNGs
7. **Cache & Serve**: Returns the ICO with aggressive caching headers

### Usage

Once deployed, the worker will automatically respond to `/favicon.ico` requests:

```
https://your-domain.com/favicon.ico
```

All other paths return a 404 response.

### Configuration

The worker requires no configuration. It automatically:

* Detects the request origin
* Fetches the homepage from the same origin
* Extracts and processes icons
* Caches results at the edge

### Technical Details

#### Icon Extraction

Uses regex-based HTML parsing to find icon `<link>` tags:

```regex
/<link[^>]*rel=["'](?:shortcut\s+icon|icon|apple-touch-icon)["'][^>]*>/gi
```

#### PNG Format Detection

Validates PNG files by checking the 8-byte signature:

```
89 50 4E 47 0D 0A 1A 0A
```

#### ICO File Structure

Generates valid ICO files with:

* ICONDIR header (6 bytes)
* ICONDIRENTRY for each image (16 bytes each)
* PNG image data (embedded without transcoding)

#### Size Handling

* Icons ≥256px are encoded as `0` in ICO directory (per ICO spec)
* Smaller icons use their actual dimensions
* Multiple resolutions are sorted smallest to largest

### Limitations

* **PNG focus**: Webflow typically uses PNG icons. GIF and JPG formats are skipped unless they can be extracted from ICO containers
* **Image transcoding**: No built-in image format conversion (keeps worker lightweight)
* **Same-origin**: Fetches icons from the same domain as the request

### Development

#### Local Testing

```bash
npm run dev
```

Visit `http://localhost:8787/favicon.ico`

#### Type Generation

```bash
npm run cf-typegen
```

#### Deployment

```bash
npm run deploy
```

### Troubleshooting

#### "No icons found" error

* Ensure your Webflow site has `<link rel="icon">` or `<link rel="apple-touch-icon">` tags in the homepage HTML
* Check that the icon URLs are accessible

#### "No valid PNG icons found" error

* Verify that your icons are in PNG format
* Check that icon URLs return valid PNG data
* Ensure icons have valid PNG headers (signature + IHDR chunk)

#### Cache issues

* Edge cache lasts 7 days
* To force refresh, clear Cloudflare cache or wait for TTL expiration
* During development, use `wrangler dev` which bypasses edge cache

## Future&#x20;

* Interpolate
  * 48x48 size in ICO&#x20;
  * 16x16 size in ICO
* SVG icon support ?&#x20;
* Desktop PNG favicon ?&#x20;
* Android chrome favicon not set  \
  Chrome selects the 192x192 icon if it is available and the 128x128 icon if it is not.
* Rel shortcut icon&#x20;

  ```
  <link rel="shortcut icon" href="/path/to/favicon">
  ```
* Consider ref&#x20;

  ```
  <link rel="icon" type="image/x-icon" href="url_to_my_favicon" /> 
  ```

<https://realfavicongenerator.net/favicon-checker>

### PWA & Web manifest&#x20;

<https://web.dev/articles/add-manifest>

|                                      |                           |                   |
| ------------------------------------ | ------------------------- | ----------------- |
| \<link> shortcut icon                | Webflow, 32x32            |                   |
| \<link> icon                         |                           |                   |
| \<link> apple-touch-icon             | Webflow, 256x256          |                   |
| \<link> apple-touch-icon-precomposed |                           |                   |
| /favicon.ico                         |                           | Hyperflow Favicon |
| /manifest.json                       | Webflow internal, not PWA |                   |

<table><thead><tr><th width="109"></th><th></th><th></th></tr></thead><tbody><tr><td>16x16</td><td></td><td></td></tr><tr><td>24x24</td><td></td><td></td></tr><tr><td>32x32</td><td>Use Webflow's uploaded icon, PNG preferred </td><td></td></tr><tr><td>48x48</td><td></td><td></td></tr><tr><td>64x64</td><td></td><td></td></tr><tr><td>128x128</td><td></td><td>Chrome selects the 192x192 icon if it is available and the 128x128 icon if it is not.</td></tr><tr><td>192x192</td><td></td><td>Chrome selects the 192x192 icon if it is available and the 128x128 icon if it is not.</td></tr><tr><td>256x256</td><td>Use Webflow's uploaded icon, PNG preferred </td><td></td></tr></tbody></table>
