透明で軽量な favicon.ico の Base64_Encode イメージ（16x16px, 1.2KB）と作り方【PHP】

<h2>Base64 Encoded Blank And Transparent favicon.ico image</h2>

> 俺様 Web API のログを見ると、毎回 [favicon](https://ja.wikipedia.org/wiki/Favicon).ico がブラウザからリクエストされている。
>
> ```
> Failed to load resource: the server responded with a status of 404 (Not Found)
> ```
>
> 無視するのも気持ち悪いので透明なアイコンが欲しい。また、CSS にも埋め込めるように Base64 で欲しい。

つまり `blank` で `transparent` で `lightweight` な 16x16 px の favicon.ico の Base64 文字列が欲しいのです。でも、オンラインのサービスで作成するのは、いささか不安なのです。

[「`php` `lightweight` `favicon.ico` `Base64` `blank`」とググって](https://www.google.com/search?q=php+lightweight+favicon.ico+Base64+blank)も、ドンピシャな情報が出てこなかったので、未来の自分のググラビリティとして。

- 合わせて読みたい
    - HTML のヘッダーで `favicon` を指定する際に `emoji` を使う方法。
        - [faviconに絵文字を使う](https://qiita.com/hidao/items/ee7b6be97c083c067dba) @ Qiita

## TL; DR <sub><sup><sub><sup>（今北産業）</sup></sub></sup></sub>

### 1. バイナリが欲しい人

- `curl -sSL https://git.io/blankfavicon16x16 -o favicon.ico`
- `wget https://git.io/blankfavicon16x16 -O favicon.ico`
  - [Blank and Transparent favicon.ico](https://github.com/KEINOS/blank_favicon_ico) @ GitHub

### 2. 文字列で欲しい人

```text:16x16pxの透過favicon.icoのBase64エンコード文字列
AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAA==
```
```html:htmlに埋め込んじゃう方法
<head>
    <link rel="icon" type="image/vnd.microsoft.icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAA==">
</head>
```

:::note info
<sup>🐒 &nbsp; 最初 `Content-Type` を `image/x-icon` にしていましたが `image/vnd.microsoft.icon` を指定するようにしました。これは [ICO](https://ja.wikipedia.org/wiki/ICO_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88)) フォーマットの画像なため「[IANA](https://ja.wikipedia.org/wiki/Internet_Assigned_Numbers_Authority) に登録された標準的な [MIME タイプ](https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%82%BF%E3%82%A4%E3%83%97)を用いることが[望ましい](https://ja.wikipedia.org/wiki/Favicon#HTML%E3%81%A7%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)」らしいからです。動いちゃうけど。</sup>
:::

### 3. `gzip` 圧縮した favicon.ico の Base64 エンコード文字列で欲しい人

```text:gzencode圧縮済みfavicon.icoのBase64_encode文字列
H4sIAAAAAAAAA2NgYARCAQEGIKnAkMHCwCDGwMCgAcRAIaAIRBwX+P///ygexaN4xGIGijAASeibMX4EAAA=
```

圧縮しなくても 1.2 KB（1,150 バイト）程度なのですが、サーバ側（PHP 側）で出力する場合、そのままだと記載が長くソースの可読性がいささか悪くなります。上記は `gzencode` で gzip 圧縮してから Base64 エンコードしたものです。記載が短く済みます。

:::note info
<sup><strong>`gzip` のまま送信する</strong><br>「ブラウザが gzip 圧縮対応なら、そのまま送信しちゃった方がさらに軽いんじゃね？」という方は鋭い！[下記コメント欄](#comment-af5eceb5fc10c5290254)をご覧ください。キャッシュが効いたスパイシーな全部盛りの使用例があります。@vf8974 Thanks!</sup>
:::

```php:解凍方法
// Gzip 圧縮 ＆ Base64 エンコード済み文字列
$base64_favicon = 'H4sIAAAAAAAAA2NgYARCAQEGIKnAkMHCwCDGwMCgAc'
                . 'RAIaAIRBwX+P///ygexaN4xGIGijAASeibMX4EAAA=';
// デコード＆解凍
$str_compressed = base64_decode($base64_favicon);
$bin_image = gzdecode($str_compressed);

// 送信
header('Content-Type: image/vnd.microsoft.icon');
header('Content-Length: ' . strlen($bin_image));
echo $bin_image;
```

ちなみに、元となる [`favicon.ico` の作成方法](#faviconico-%E3%81%AE-ico-%E7%94%BB%E5%83%8F%E3%81%AE%E4%BD%9C%E6%88%90%E3%81%A8-base64-%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89)は TS;DR 参照。

## TS; DR （限りなく透明なソースコード）

```php:具体的な使用例
<?php

// Main
if (isRequestFaviconIco()) {
    echoFaviconAndExit();
}

// Functions
function echoFaviconAndExit()
{
    $bin_image = getFaviconImage();

    header('Content-Type: image/vnd.microsoft.icon');
    header('Content-Length: ' . strlen($bin_image));
    echo $bin_image;

    exit(0);
}

function getFaviconImage()
{
    $base64_favicon = 'H4sIAAAAAAAAA2NgYARCAQEGIKnAkMHCwCDGwMCgAc' .
                      'RAIaAIRBwX+P///ygexaN4xGIGijAASeibMX4EAAA=';
    $str_compressed = base64_decode($base64_favicon);

    return gzdecode($str_compressed);
}

function isRequestFaviconIco()
{
    if (isset($_SERVER['REQUEST_URI']) && ($_SERVER['REQUEST_URI']==='/favicon.ico')) {
        return true;
    }

    return false;
}
```
```shell-session:元favicon.icoイメージのHexDump
$ hexdump -C favicon.ico
00000000  00 00 01 00 01 00 10 10  00 00 01 00 20 00 68 04  |............ .h.|
00000010  00 00 16 00 00 00 28 00  00 00 10 00 00 00 20 00  |......(....... .|
00000020  00 00 01 00 20 00 00 00  00 00 00 00 00 00 00 00  |.... ...........|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 ff ff  |................|
00000040  ff 00 ff ff ff 00 ff ff  ff 00 ff ff ff 00 ff ff  |................|
*
00000440  00 00 ff ff 00 00 ff ff  00 00 ff ff 00 00 ff ff  |................|
*
00000470  00 00 ff ff 00 00 ff ff  00 00 ff ff 00 00        |..............|
0000047e

```
```shell-session:サイズは1.2KB
$ ls -l favicon.ico
-rw-r--r--@ 1 admin  staff  1150 11  5 15:22 favicon.ico

$ curl -s --head http://localhost:8080/favicon.ico | grep Content
Content-Type: image/vnd.microsoft.icon
Content-Length: 1150
```

### `favicon.ico` の ICO 画像の作成と Base64 エンコード

PHP の [GD](https://ja.wikipedia.org/wiki/GD_Graphics_Library) と、イメージを ICO フォーマットに変換する [PHP ICO](https://github.com/chrisbliss18/php-ico) クラスを使って ICO 画像を作成してみました。処理内容は以下の通り。

1. 16x16 px の TrueColor キャンバスを白で埋める。
2. アルファ値を MAX（127）にして透過にする。
3. PHP ICO で ICO フォーマットに変換
4. `gzencode` で圧縮
5. `base64_encode` でテキスト化

- [オンラインでソースコードと実行結果をみる](https://paiza.io/projects/ubhgXR_RX0uyjAJNJx18Tw) @ paiza.IO
- リポジトリもあるでよ
  - [Blank and Transparent favicon.ico](https://github.com/KEINOS/blank_favicon_ico) @ GitHub

## 所感

Docker コンテナ上の [PHP のビルトイン Web サーバー](https://www.php.net/manual/ja/features.commandline.webserver.php)で動かしている簡単な Web API を [`ngrok` や `serveo` 経由で公開](https://www.google.com/search?q=site:qiita.com+ngrok+serveo)する際に「favicon.ico ないよ 404 エラー」が気持ち悪かったので、これくらいは返してあげようと思いました。

普通にサイト上に favicon.ico ファイルを置いておけばいいのですが、[ルータースクリプト](https://www.php.net/manual/ja/features.commandline.webserver.php)（`php -S localhost:8080 /app/index.php` の `index.php`）を指定しているので「どうせルータースクリプト内で分岐するなら、スクリプトで吐き出しちゃえばいいや」と思いました。

しかし、**オンラインのサイトで作成した favicon.ico で、変なデータを入れられるのが嫌だった**ので、16x16px の透過 PNGを ICO 化したデータが欲しかったのです。

[「`php` `lightweight` `favicon.ico` `Base64` `blank`」とググって](https://www.google.com/search?q=php+lightweight+favicon.ico+Base64+blank)も、ドンピシャな情報が出てきませんでした。

おそらく、苦労したことを忘れて同じことを検索すると思うので、コピペピピックな未来の自分へ向けた記事です。