# google-webfonts-helper [![Uptime Robot status](https://img.shields.io/uptimerobot/status/m793130668-adecafe120852713ed46d6c6)](https://gwfh.mranftl.com) [![Uptime Robot ratio (30 days)](https://img.shields.io/uptimerobot/ratio/m793130668-adecafe120852713ed46d6c6)](https://gwfh.mranftl.com) [![GitHub Sponsors](https://img.shields.io/github/sponsors/majodev)](https://github.com/sponsors/majodev) > A Hassle-Free Way to Self-Host Google Fonts ✅ **[https://gwfh.mranftl.com](https://gwfh.mranftl.com)** ## Current Sponsors > *Help me keep this service alive by [sponsoring me](https://github.com/sponsors/majodev). Thank you. ❤️* [](https://sponsors.mranftl.com/profile/0) [](https://sponsors.mranftl.com/profile/1) [](https://sponsors.mranftl.com/profile/2) [](https://sponsors.mranftl.com/profile/3) [](https://sponsors.mranftl.com/profile/4) [](https://sponsors.mranftl.com/profile/5) [](https://sponsors.mranftl.com/profile/6) [](https://sponsors.mranftl.com/profile/7) [](https://sponsors.mranftl.com/profile/8) [](https://sponsors.mranftl.com/profile/9) [](https://sponsors.mranftl.com/profile/10) [](https://sponsors.mranftl.com/profile/11) [](https://sponsors.mranftl.com/profile/12) [](https://sponsors.mranftl.com/profile/13) [](https://sponsors.mranftl.com/profile/14) [](https://sponsors.mranftl.com/profile/15) [](https://sponsors.mranftl.com/profile/16) [](https://sponsors.mranftl.com/profile/17) [](https://sponsors.mranftl.com/profile/18) [](https://sponsors.mranftl.com/profile/19) [](https://sponsors.mranftl.com/profile/20) [](https://sponsors.mranftl.com/profile/21) [](https://sponsors.mranftl.com/profile/22) [](https://sponsors.mranftl.com/profile/23) [](https://sponsors.mranftl.com/profile/24) [](https://sponsors.mranftl.com/profile/25) [](https://sponsors.mranftl.com/profile/26) [](https://sponsors.mranftl.com/profile/27) [](https://sponsors.mranftl.com/profile/28) [](https://sponsors.mranftl.com/profile/29) [](https://sponsors.mranftl.com/profile/30) [](https://sponsors.mranftl.com/profile/31) [](https://sponsors.mranftl.com/profile/32) [](https://sponsors.mranftl.com/profile/33) [](https://sponsors.mranftl.com/profile/34) [](https://sponsors.mranftl.com/profile/35) [](https://sponsors.mranftl.com/profile/36) [](https://sponsors.mranftl.com/profile/37) [](https://sponsors.mranftl.com/profile/38) [](https://sponsors.mranftl.com/profile/39) [](https://sponsors.mranftl.com/profile/40) [](https://sponsors.mranftl.com/profile/41) [](https://sponsors.mranftl.com/profile/42) [](https://sponsors.mranftl.com/profile/43) [](https://sponsors.mranftl.com/profile/44) [](https://sponsors.mranftl.com/profile/45) [](https://sponsors.mranftl.com/profile/46) [](https://sponsors.mranftl.com/profile/47) [](https://sponsors.mranftl.com/profile/48) [](https://sponsors.mranftl.com/profile/49) [](https://sponsors.mranftl.com/profile/50) [](https://sponsors.mranftl.com/profile/51) [](https://sponsors.mranftl.com/profile/52) [](https://sponsors.mranftl.com/profile/53) [](https://sponsors.mranftl.com/profile/54) [](https://sponsors.mranftl.com/profile/55) [](https://sponsors.mranftl.com/profile/56) [](https://sponsors.mranftl.com/profile/57) [](https://sponsors.mranftl.com/profile/58) [](https://sponsors.mranftl.com/profile/59) [](https://sponsors.mranftl.com/profile/60) [](https://sponsors.mranftl.com/profile/61) [](https://sponsors.mranftl.com/profile/62) [](https://sponsors.mranftl.com/profile/63) [](https://sponsors.mranftl.com/profile/64) [](https://sponsors.mranftl.com/profile/65) [](https://sponsors.mranftl.com/profile/66) [](https://sponsors.mranftl.com/profile/67) [](https://sponsors.mranftl.com/profile/68) ## ToC - [google-webfonts-helper ](#google-webfonts-helper---) - [Current Sponsors](#current-sponsors) - [ToC](#toc) - [Give it a try: https://gwfh.mranftl.com](#give-it-a-try-httpsgwfhmranftlcom) - [Running gwfh on your own server](#running-gwfh-on-your-own-server) - [Development](#development) - [Quickstart](#quickstart) - [Production build](#production-build) - [JSON API](#json-api) - [GET `/api/fonts`](#get-apifonts) - [GET `/api/fonts/[id]?subsets=latin,latin-ext`](#get-apifontsidsubsetslatinlatin-ext) - [GET `/api/fonts/[id]?download=zip&subsets=latin&formats=woff,woff2&variants=regular`](#get-apifontsiddownloadzipsubsetslatinformatswoffwoff2variantsregular) - [History](#history) - [License](#license) ## Give it a try: [https://gwfh.mranftl.com](https://gwfh.mranftl.com) This service might be handy if you want to host a specific [Google font](https://fonts.google.com/) on your **own** server: * font style and charset customization * CSS snippets * `.eot`, `.woff`, `.woff2`, `.svg`, `.ttf` font file formats download (zipped). [![pic running](https://mranftl.com/static/apps/google-webfonts-helper/full_view.png)](https://gwfh.mranftl.com) ## Running gwfh on your own server I provide prebuilt Docker images via [GitHub Packages](https://github.com/majodev/google-webfonts-helper/pkgs/container/google-webfonts-helper). You can use them as follows: ```bash # See https://developers.google.com/fonts/docs/developer_api for creating your own API-Key. docker run -e GOOGLE_FONTS_API_KEY= -p 8080:8080 ghcr.io/majodev/google-webfonts-helper: # Express server listening on 8080, in production mode ``` ## Development ### Quickstart Do this to setup a development environment: ```bash # Ensure to set the GOOGLE_FONTS_API_KEY env var inside your own gitignored .env file # See https://developers.google.com/fonts/docs/developer_api for creating your own API-Key. echo "GOOGLE_FONTS_API_KEY=" > .env # Start up the development docker container (multistage Dockerfile, stage 1 only) ./docker-helper.sh --up # [+] Running 1/0 # ⠿ Container gwfh-service-1 Running # node@3b506a285f7f:/app$ # within this development container: node$ yarn --pure-lockfile node$ ./node_modules/.bin/bower install # start development server node$ grunt serve # [...] # Express server listening on 9000, in development mode # The application is now available at http://127.0.0.1:9000 (watching for code changes) # start production server (same command as within the final docker multistage build) node$ grunt build node$ NODE_ENV=production node dist/server/app.js # Express server listening on 8080, in production mode ``` ### Production build If you want to build and run your own **production** container locally: ```bash # Build the production docker container (final stage) docker build . -t # Run it (if you have previously started the development container, halt it!) ./docker-helper.sh --halt docker run -e GOOGLE_FONTS_API_KEY= -p 8080:8080 # Express server listening on 8080, in production mode ``` To mitigate security issues especially with the projects' deprecated dependencies, the final image is based on a minimal container image. It runs rootless and has no development dependencies. ## JSON API The API is public, feel free to use it directly (rate-limits may apply). ### GET `/api/fonts` Returns a list of all fonts, sorted by popularity. E.g. `curl https://gwfh.mranftl.com/api/fonts`: ```json [{ "id": "open-sans", "family": "Open Sans", "variants": ["300", "300italic", "regular", "italic", "600", "600italic", "700", "700italic", "800", "800italic"], "subsets": ["devanagari", "greek", "latin", "cyrillic-ext", "cyrillic", "greek-ext", "vietnamese", "latin-ext"], "category": "sans-serif", "version": "v10", "lastModified": "2014-10-17", "popularity": 1, "defSubset": "latin", "defVariant": "regular" } [...] ] ``` ### GET `/api/fonts/[id]?subsets=latin,latin-ext` Returns a font with urls to the actual font files google's servers. `subsets` is optional (will serve the `defSubset` if unspecified). E.g. `curl "https://gwfh.mranftl.com/api/fonts/modern-antiqua?subsets=latin,latin-ext"` (the double quotes are important as query parameters may else be stripped!): ```json { "id": "modern-antiqua", "family": "Modern Antiqua", "variants": [{ "id": "regular", "eot": "https://fonts.gstatic.com/s/modernantiqua/v6/8qX_tr6Xzy4t9fvZDXPkhzThM-TJeMvVB0dIsYy4U7E.eot", "fontFamily": "'Modern Antiqua'", "fontStyle": "normal", "fontWeight": "400", "woff": "https://fonts.gstatic.com/s/modernantiqua/v6/8qX_tr6Xzy4t9fvZDXPkh1bbnkJREviNM815YSrb1io.woff", "local": ["Modern Antiqua Regular", "ModernAntiqua-Regular"], "ttf": "https://fonts.gstatic.com/s/modernantiqua/v6/8qX_tr6Xzy4t9fvZDXPkhxr_S_FdaWWVbb1LgBbjq4o.ttf", "svg": "https://fonts.gstatic.com/l/font?kit=8qX_tr6Xzy4t9fvZDXPkh0sAoW0rAsWAgyWthbXBUKs#ModernAntiqua", "woff2": "https://fonts.gstatic.com/s/modernantiqua/v6/8qX_tr6Xzy4t9fvZDXPkh08GHjg64nS_BBLu6wRo0k8.woff2" }], "subsets": ["latin", "latin-ext"], "category": "display", "version": "v6", "lastModified": "2014-08-28", "popularity": 522, "defSubset": "latin", "defVariant": "regular", "subsetMap": { "latin": true, "latin-ext": true }, "storeID": "latin-ext_latin" } ``` ### GET `/api/fonts/[id]?download=zip&subsets=latin&formats=woff,woff2&variants=regular` Download a zipped archive with all `.eot`, `.woff`, `.woff2`, `.svg`, `.ttf` files of a specified font. The query parameters `formats` and `variants` are optional (includes everything if no filtering is applied). is E.g. `curl -o fontfiles.zip "https://gwfh.mranftl.com/api/fonts/lato?download=zip&subsets=latin,latin-ext&variants=regular,700&formats=woff"` (the double quotes are important as query parameters may else be stripped!) ## History > 2025: * Switch to `node:22` for the final image. * Adds support for linux/arm64 architecture (patches [imagemin/optipng-bin](https://github.com/imagemin/optipng-bin/pull/128)) > 2024: * Switch to `node:20` for the final image. > 2023: * Project upgraded to be compatible with Node.js v18+. * Automated prebuilt Docker images via [GitHub Actions](https://github.com/majodev/google-webfonts-helper/actions). * `/server` was fully refactored/modernized (async/await) and now compiles with TypeScript. * Switch to `node:18` for the final image. * `/client` can still be considered very legacy Angular code. > 2022: This service was mostly on life-support, most of its code and dependencies can be considered deprecated. The current docker image wrapping `node@v0.10.44` runs rootless and is hopefully enough to keep the bandits out. API attack surface should be minimal anyways. > 2014: This service was originally a prototype I've created to get familiar with Angular and Express. All magic by [generator-angular-fullstack](https://github.com/DaftMonk/generator-angular-fullstack). See [my note here](http://mranftl.com/2014/12/23/self-hosting-google-web-fonts/). Idea originally by Clemens Lang who created an [awesome bash script](https://neverpanic.de/blog/2014/03/19/downloading-google-web-fonts-for-local-hosting/) to download Google fonts in all formats. ## License (c) Mario Ranftl [MIT License](http://majodev.mit-license.org/) [Google Fonts Open Source Font Attribution](https://fonts.google.com/attribution)