Serve custom and preset Favicons (website icons) via HTTP(s)
queriable via dynamic URL subdomains and/or HTTP basic auth
This project was created with the main purpose of covering some Favicon issues on Bitwarden/bitwarden_rs:
- Bitwarden incorrectly assumes that domains serve their Favicons on the root-level (i.e. at
http(s)://<domain>/favicon.ico
):- This behaviour is discouraged by W3C's Technical Architecture Group (issue: siteData-36);
<link rel="icon">
elements seem to be ignored, which means that websites that use non-root locations won't have an icon loaded by Bitwarden (e.g. Uphold, which hosts its favicon under /img/favicon.ico);
- Having custom icons for either reachable or non-reachable sites is currently not supported;
- There are privacy concerns regarding website icons:
- When Bitwarden displays a login item associated with a website, it will issue requests to
icons.bitwarden.net
, leaking information on the websites you access including private (intranet) hosts.
- When Bitwarden displays a login item associated with a website, it will issue requests to
Upon load, serve-favicons
scans the icons/
folder for directories containing icon (.ico
) files. These directories will be used internally as "namespaces" and can have arbitrary names as long as they are valid DNS subdomain names, as specified in RFC1123 — as a rule of thumb, stick to concise lowercase alphanumeric characters.
Loaded icons will be served by a simple HTTP server on port 8080
by default, which can be retrieved by querying either:
http://<icon>.<domain>:<port>
http://<icon>@<host>:<port>
Where:
- "
<icon>
" is the resource name, e.g. "github
" for the GitHub icon; - "
<domain>
" is a configured URL domain/subdomain when loaded behind a reverse proxy, e.g. "icons.example.com
" (more on this under the configurations section, below); - "
<host>
" is the server's bound host or IP address, e.g. "localhost
" or "127.0.0.1
"; - "
<port>
" is the server's listening port (or an external port, for proxied requests).
Requests to either the root (/
) or the favicon (/favicon.ico
) URL path will be answered with a 200
HTTP response with image/x-icon
content type for matching icons, or a 404
for missing ones.
If desired (e.g. for icons with multiple variants), it is also possible to query for icons under a specific namespace, with either:
http://<icon>.<namespace><domain>:<port>
http://<icon>:<namespace>@<host>:<port>
In order to use serve-favicons
with Bitwarden, you'll need to run the server behind a reverse proxy (ideally with HTTPS) on a publicly accessible domain address. DNS resolution should also be configured so that queries for subdomains resolve to the same host.
Let's assume our domain is my-super-domain.com
and that we want to use icons.my-super-domain.com
as a public entrypoint to serve-favicons
. Example DNS configuration on Cloudflare:
💡 To test that our deployment is working, check with:
$ curl -sLw "%{http_code}\\n" https://rss.icons.my-super-domain.com -o /dev/null # should print "200" if OK
Bitwarden only loads the icon for the first domain, so the trick here is to use our server as the first URL in the list and mark it so that it never matches the domain for password detection:
By saving this entry, we can confirm that the icon is loaded from serve-favicons
:
Now, simply repeat the above step for the remaining entries you wish to tweak, by adding a reference to an existing icon as the first URI and setting match detection to "Never".
On boot, the following default configuration will be used:
{
"domain": "icons.local",
"port": 8080,
"icons": {
"basedir": "icons",
},
}
Field | Description | Default value |
---|---|---|
domain |
domain to be used to match subdomain-based queries (e.g. "icons.my-super-domain.com ", from Integrating with Bitwarden above) |
icons.local |
port |
server listening port | 8080 |
icons.basedir |
location of the root icons directory (will default to the provided icons bundle, next to server.js ) |
icons |
These defaults can be overridden by an external config.json
file, whose path can be set via the ICONS_CONFIG_DIR
environment variable — see sample/config.json for an example of what could be loaded with "ICONS_CONFIG_DIR=sample
".
By default, DEBUG
logs will be disabled. If you wish to turn on these logs, set the ICONS_DEBUG_MODE
environment variable to something truthy (e.g. "true"
, or 1
).
$ npm start
Alpine-based container image available at sykkro/serve-favicons
.
$ docker run -p 8080:8080 --rm docker.io/sykkro/serve-favicons:latest
The architectures supported by this image are:
OS/Arch | Approx. size |
---|---|
linux/amd64 | 28 MB |
linux/arm64 | 28 MB |
linux/arm/v7 | 27 MB |
$ npm install && npm run lint && npm run test
# multi-platform build with buildx
$ docker buildx build \
--platform=linux/amd64,linux/arm/v7,linux/arm64 \
--output "type=image,push=false" \
-t sykkro/serve-favicons:dev .
Most icons were based on the excellent work from aegis-icons and converted to .ico
using ImageMagick's mogrify
command:
mogrify -quiet -format ico -density 256x256 -background none -define icon:auto-resize=256 -path output icons/*.png
As with the aegis-icons project, this project includes material that may be protected as a trademark in some jurisdictions. Trademarks seen here in "serve-favicons" project does not indicate endorsement to the brands. If you want to use this project and choose to expose icons publicly, you have to ensure that you have the legal right to do so and that you do not infringe any trademark rights. Please do not use brand based icons for any purpose except to represent the company, product, or service to which they refer. The CC0 license applies only to templates, texts, codes and some of the icons. Everything else is either licensed (see the credits) or property of trademark owners. If you're owner of certain trademark(s) and want icon taken down for whatever reason, please make a new issue.