Skip to content

thunderbird/avatars

Repository files navigation

avatars

Cloudflare Worker that serves sender-domain avatar images for Thunderbird webmail.

The service accepts a normalized domain on avatars.thunderbird.net and always returns a PNG, falling back to a 1x1 transparent pixel on errors:

https://avatars.thunderbird.net/example.com?v=3

The optional v query parameter is a cache version chosen by the caller. Changing it forces a fresh edge/browser cache entry when avatar lookup behavior changes. It is not sent to upstream providers.

Behavior

  • Accepts only GET, HEAD, and OPTIONS. Other methods get a 405 fallback.
  • Normalizes and validates the domain before making any upstream request.
  • Fetches favicons only from https://www.google.com/s2/favicons. Because Google reliably redirects this endpoint to its CDN, redirects are followed and the final response is required to come from www.google.com or a *.gstatic.com subdomain over HTTPS. Bare gstatic.com and any http: downgrade are rejected.
  • Returns only PNG bodies up to 256 KiB, validating the PNG signature, chunk structure, IHDR/PLTE/IDAT/IEND ordering, color-type/bit-depth combinations, and CRC-32 of every chunk before responding or caching. Provider response headers are not trusted.
  • Successful responses, validation failures, and Google 4xx responses are all cached in Cloudflare's default Cache API under a versioned key (/__cache/<provider-version>/<domain>). Validation failures are negative- cached for one hour; transient network failures (502) are not cached.
  • HEAD requests share the GET cache and populate it on miss, so a HEAD costs no more than a GET and never bypasses the edge.
  • Sends wildcard CORS headers, X-Content-Type-Options: nosniff, and Cross-Origin-Resource-Policy: cross-origin on every PNG response.
  • Keeps Worker observability logging disabled.

Known limitations

  • Google's favicon service responds with a generic "globe" placeholder for domains it does not know about. The worker has no signal to distinguish this from a real favicon and will cache the placeholder under the requested domain for the configured TTL. Operators that want to fail-closed for unknown domains should change the v query parameter or bump AVATAR_CACHE_PROVIDER_VERSION after fixing the heuristic.

Development

Use Node.js 24 or newer.

npm ci
npm run typecheck
npm test

Run locally with Wrangler:

npx wrangler dev

Deployment

The production custom domain avatars.thunderbird.net is attached to the Worker once in Cloudflare using human credentials. The steady-state GitHub Actions deploy updates only the Worker script, so the CI token does not need DNS or Worker route permissions.

Required repository secrets:

  • CLOUDFLARE_ACCOUNT_ID
  • CLOUDFLARE_API_TOKEN

The deploy token is scoped with only Workers script write/edit permissions.

About

Sender-domain avatar proxy for Thunderbird webmail

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors