unifont
  • All fonts Browse or filter the whole catalogue
  • Compare providers One family, every CDN, side by side
  • Documentation Install, providers, custom providers
  • HTTP API The endpoints this site is built on

Self-hosting the proxy

What the proxy will and won't do, the endpoints it serves, and how to deploy your own.

The proxy is a small Nitro app in the proxy/ directory of the unifont repository. Provider APIs send no CORS headers, so it's how browsers and web containers reach them at all.

Deploy your own if you resolve fonts in a browser or a web container at runtime and you need that to keep working. Nothing else needs it: on a server, in a worker, in CI, or at build time (what @nuxt/fonts and fontless do) the provider APIs are called directly.

What it will and won't do

It's read-only, and narrow on purpose:

  • GET and HEAD only, and only the routes below. Anything else is a 404.
  • Only the query parameters named per route. Everything else is dropped before the upstream call.
  • Only the upstream content-type is carried back. No other upstream header is replayed.
  • No credentials, no cookies, no auth, in either direction.
  • Font files never go through it. They come from the provider's own CDN, which already allows cross-origin requests.

Endpoints

GET / returns the endpoint list as JSON. Every route is versioned:

RouteUpstreamQuery
/adobe/v1/kit/:idtypekit.com/api/v1/json/kits/:id/published
/adobe/v1/kit-css/:iduse.typekit.net/:id.css
/bunny/v1/listfonts.bunny.net/list
/bunny/v1/cssfonts.bunny.net/cssfamily
/fontshare/v1/fontsapi.fontshare.com/v2/fontsoffset, limit
/fontshare/v1/cssapi.fontshare.com/v2/cssf[]
/fontsource/v1/fontsapi.fontsource.org/v1/fonts
/fontsource/v1/fonts/:idapi.fontsource.org/v1/fonts/:id
/fontsource/v1/variable/:idapi.fontsource.org/v1/variable/:id
/google/v1/fontsfonts.google.com/metadata/fonts
/google/v1/cssfonts.googleapis.com/css2family, text, icon_names, format
/google/v1/iconsfonts.google.com/metadata/icons
/google/v1/iconfonts.googleapis.com/iconfamily, format

format is one of woff2 (the default), woff, ttf or eot. Each one maps to the user agent Google expects for that format.

Responses carry cache-control (public, max-age = s-maxage, plus stale-while-revalidate), an etag, and 304 support. Metadata is cached for six hours, generated CSS for a day, Adobe kits for five minutes.

Deploying it

Nothing here needs a database, credentials or environment variables.

  • Vercel, Netlify, Cloudflare: create a project with proxy/ as the root directory. Nitro picks up the preset from the environment, and the build command is pnpm build.
  • Node: pnpm build, then run .output/server/index.mjs.

Once it's up, GET / should return the endpoint list. Pointing apiBase at it is the only change your application needs:

ts

const unifont = await createUnifont([providers.google()], {
  apiBase: 'https://fonts.example.com',
})

A fresh deployment is cold, so the first request for each endpoint waits on the upstream. The cache lives in the process, which on a serverless host means most of the work is done by the CDN in front. That's what the cache-control headers are for.