> [!IMPORTANT] > If you were on v3 or earlier and want to upgrade to v4, check out the [migration guide](docs/UPGRADE_TO_V4.md). If you ended up here, chances are you would like to replace your HTTP server's default error pages with something more original and eye-catching. That is exactly what this project is designed for - it handles this with minimal effort on your part. It includes: - A collection of HTTP error page designs (each with a unique look), along with the ability to use your own custom templates - A lightweight HTTP server for serving these pages that integrates easily into your existing infrastructure - A utility for pre-rendering static HTTP error pages Key features: - Both the HTTP server and the static generator are written in Go with zero third-party runtime dependencies (stdlib only - hardcore mode) * Supports HTTP/1.1 and HTTP/2 (h2c - cleartext, no TLS required) * Returns error responses in the appropriate format (HTML, JSON, XML, plain text) based on client requests * Gzip compression for all response formats * HTML pages support localization (15+ languages), responsive design (mobile-friendly), and are fully self-contained - all styles and images are embedded directly in the HTML, without loading any external resources * Go template-based templating engine - Ships as pre-built binaries, a minimal Docker image (rootless, scratch-based), and a ready-to-use Helm chart for Kubernetes - Works out of the box with popular reverse proxies and ingress controllers (Nginx, Traefik, etc.) ## ๐Ÿช‚ Built-in templates The following templates are built-in and available for use without any additional setup/files: | Template name | Preview (light) | Preview (dark) | |:-----------------:|------------------------------------------------|-----------------------------------------------| | `app-down` | [![][app-down-light]][app-down-link] | [![][app-down-dark]][app-down-link] | | `cats` | [![][cats-light]][cats-link] | [![][cats-dark]][cats-link] | | `connection` | [![][connection-light]][connection-link] | [![][connection-dark]][connection-link] | | `ghost` | [![][ghost-light]][ghost-link] | [![][ghost-dark]][ghost-link] | | `hacker-terminal` | [![][hacker-terminal]][hacker-terminal-link] | [![][hacker-terminal]][hacker-terminal-link] | | `l7` | [![][l7-light]][l7-link] | [![][l7-dark]][l7-link] | | `lost-in-space` | [![][lost-in-space-light]][lost-in-space-link] | [![][lost-in-space-dark]][lost-in-space-link] | | `noise` | [![][noise]][noise-link] | [![][noise]][noise-link] | | `orient` | [![][orient-light]][orient-link] | [![][orient-dark]][orient-link] | | `shuffle` | [![][shuffle-light]][shuffle-link] | [![][shuffle-dark]][shuffle-link] | | `win98` | [![][win98-light]][win98-link] | [![][win98-dark]][win98-link] | > [!NOTE] > The `cats` template is the only one of those that fetches resources (the actual cat pictures) from external > servers - all other templates are self-contained. > [!TIP] > If you need the **pre-rendered static error pages pack**, you can download it as a [zip][pages-pack-zip] or > [tar.gz][pages-pack-tar-gz] archive. [app-down-link]:https://tarampampam.github.io/error-pages/app-down/404.html [app-down-light]:https://habrastorage.org/webt/4d/55/04/4d5504d21fc8dd64e9418aef387ca0b6.png [app-down-dark]:https://habrastorage.org/webt/ff/f5/1e/fff51e593cebcf9db3280c8830218e00.png [cats-link]:https://tarampampam.github.io/error-pages/cats/404.html [cats-light]:https://github.com/user-attachments/assets/7bea967e-a427-4ba2-a3a3-d71b9986ecfc [cats-dark]:https://github.com/user-attachments/assets/f9b945b2-3e19-44d5-842b-0c43c55d9b70 [connection-link]:https://tarampampam.github.io/error-pages/connection/404.html [connection-light]:https://github.com/tarampampam/error-pages/assets/7326800/099ecc2d-e724-4d9c-b5ed-66ddabd71139 [connection-dark]:https://github.com/tarampampam/error-pages/assets/7326800/3f03dc1b-c1ee-4a91-b3d7-e3b93c79020e [ghost-link]:https://tarampampam.github.io/error-pages/ghost/404.html [ghost-dark]:https://github.com/tarampampam/error-pages/assets/7326800/714482ab-f8c1-4455-8ae8-b2ae78f7a2c6 [ghost-light]:https://github.com/tarampampam/error-pages/assets/7326800/f253dfe7-96a0-4e96-915b-d4c544d4a237 [hacker-terminal-link]:https://tarampampam.github.io/error-pages/hacker-terminal/404.html [hacker-terminal]:https://github.com/tarampampam/error-pages/assets/7326800/c197fc35-0844-43d0-9830-82440cee4559 [l7-link]:https://tarampampam.github.io/error-pages/l7/404.html [l7-dark]:https://github.com/tarampampam/error-pages/assets/7326800/18e43ea3-6389-4459-be41-0fc6566a073f [l7-light]:https://github.com/tarampampam/error-pages/assets/7326800/05f26669-94ec-40ce-8d67-a199cde54202 [lost-in-space-link]:https://tarampampam.github.io/error-pages/lost-in-space/404.html [lost-in-space-dark]:https://github.com/tarampampam/error-pages/assets/7326800/debf87c0-6f27-41a8-b141-ee3464cbd6cc [lost-in-space-light]:https://github.com/tarampampam/error-pages/assets/7326800/c347e63d-13a7-46d4-81b9-b25266819a1d [noise-link]:https://tarampampam.github.io/error-pages/noise/404.html [noise]:https://github.com/tarampampam/error-pages/assets/7326800/4cc5c3bd-6ebb-4e96-bee8-02d4ad4e7266 [orient-link]:https://tarampampam.github.io/error-pages/orient/404.html [orient-dark]:https://github.com/tarampampam/error-pages/assets/7326800/bc2b0dad-c32c-4628-98f6-e3eab61dd1f2 [orient-light]:https://github.com/tarampampam/error-pages/assets/7326800/8fc0a7ea-694d-49ce-bb50-3ea032d52d1e [shuffle-link]:https://tarampampam.github.io/error-pages/shuffle/404.html [shuffle-dark]:https://github.com/tarampampam/error-pages/assets/7326800/7504b7c3-b0cb-4991-9ac2-759cd6c50fc0 [shuffle-light]:https://github.com/tarampampam/error-pages/assets/7326800/d2a73fc8-cf5f-4f42-bff8-cce33d8ae47e [win98-link]:https://tarampampam.github.io/error-pages/win98/404.html [win98-dark]:https://habrastorage.org/webt/bu/zt/5w/buzt5wsr-wixk0y8xjbxvepj0a8.png [win98-light]:https://habrastorage.org/webt/pg/e8/f1/pge8f1ahyspmgu9vyh0jigvq_es.png ## ๐Ÿš€ Installation Download the latest binary for your OS/architecture from the [releases page][latest-release], or use the Docker image: | Registry | Image | |-----------------------------------|-----------------------------------| | [GitHub Container Registry][ghcr] | `ghcr.io/tarampampam/error-pages` | | [Quay.io][quay] (mirror) | `quay.io/tarampampam/error-pages` | | [Docker Hub][docker-hub] (mirror) | `tarampampam/error-pages` | > [!WARNING] > Using the `latest` tag for Docker images is strongly discouraged, as it may introduce backward-incompatible changes > during **major** upgrades. Use versioned tags in the `X`, `X.Y`, or `X.Y.Z` format instead. > [!IMPORTANT] > The app is distributed as two separate binaries - `error-pages` (HTTP server) and `builder`. Docker tags follow this > convention: > - `X.Y.Z` (and `X.Y`, `X`) - includes the HTTP server > - `X.Y.Z-builder` (and `X.Y-builder`, `X-builder`) - includes the builder and a pre-rendered error pages pack Supported image architectures - `linux/amd64`, `linux/arm/v7`, `linux/arm64`, `linux/ppc64le`, `linux/s390x`. All images are signed with [Cosign][cosign] using keyless signing (GitHub OIDC). ### ๐Ÿ“ฆ Helm chart A Helm chart for Kubernetes is included with each release ([download][latest-helm-chart]), published on [Artifact Hub][artifacthub], and also available via an OCI registry (Helm v3.8+ required): ```shell helm install error-pages \ oci://ghcr.io/tarampampam/error-pages/charts/error-pages \ --version X.Y.Z ``` All supported chart values, examples, and usage instructions can be found at [Artifact Hub][artifacthub]. > Helm chart sources are located in the [deploy/helm](deploy/helm) directory of the repository. [latest-release]:https://github.com/tarampampam/error-pages/releases/latest [ghcr]:https://github.com/tarampampam/error-pages/pkgs/container/error-pages [docker-hub]:https://hub.docker.com/r/tarampampam/error-pages [quay]:https://quay.io/repository/tarampampam/error-pages?tab=tags [pages-pack-zip]:https://github.com/tarampampam/error-pages/releases/latest/download/error-pages-static.zip [pages-pack-tar-gz]:https://github.com/tarampampam/error-pages/releases/latest/download/error-pages-static.tar.gz [cosign]:https://github.com/sigstore/cosign [latest-helm-chart]:https://github.com/tarampampam/error-pages/releases/latest/download/helm-chart.tgz [artifacthub]:https://artifacthub.io/packages/helm/error-pages/error-pages ## ๐Ÿ›  Integration guides - **Standalone** server or builder usage: - [๐Ÿš€ Start the HTTP server with a custom template (theme)](docs/guides/theming.md) - [๐Ÿš€ Generate error pages using built-in or custom templates](docs/guides/builder.md) - With **Nginx**: - [๐Ÿš€ Customize error pages in your own Nginx Docker image](docs/guides/nginx_image.md) - [๐Ÿš€ Use Nginx as a reverse proxy with custom error pages](docs/guides/nginx_upstream.md) - With **Caddy**: - [๐Ÿš€ Customize error pages in your own Caddy Docker image](docs/guides/caddy_image.md) - [๐Ÿš€ Use Caddy as a reverse proxy with custom error pages](docs/guides/caddy_upstream.md) - With **Traefik**: - [๐Ÿš€ Use Traefik with a local Docker Compose setup](docs/guides/traefik_docker.md) - With **Kubernetes**: - [๐Ÿš€ Use error-pages as the **ingress-nginx** default backend](docs/guides/k8s_ingress_nginx.md) - [๐Ÿš€ Use error-pages with **Traefik** in Kubernetes](docs/guides/k8s_ingress_traefik.md) - [๐Ÿš€ Use error-pages with **NGINX Gateway Fabric** in Kubernetes](docs/guides/k8s_ingress_ngf.md) - [๐Ÿš€ Use error-pages with **Envoy Gateway** in Kubernetes](docs/guides/k8s_ingress_envoy.md) - [๐Ÿš€ Use error pages with **HAProxy Ingress** in Kubernetes](docs/guides/k8s_ingress_haproxy.md) ## ๐Ÿฆพ Performance Measured on loopback (`127.0.0.1`), single connection, no artificial load (`wrk -t1 -c1 -d5s --latency http://127.0.0.1:8080/...`, less is better): | Format | p50 (typical response time) | p90 (90% of responses complete within this time) | |------------:|:---------------------------:|:------------------------------------------------:| | HTML | **121 ยตs** | 262 ยตs | | JSON | **51 ยตs** | 75 ยตs | | XML | **48 ยตs** | 73 ยตs | | Plain text | **47 ยตs** | 68 ยตs | | HTML + gzip | **2.4 ms** | 3.1 ms | | JSON + gzip | **256 ยตs** | 510 ยตs | > [!NOTE] > HTML responses are large (full rendered template, ~65 KB), which is why gzip compression takes noticeably more > time there. JSON/XML/text are compact structured responses, so they are fastest overall. ## ๐Ÿ’ป Command-line usage For detailed instructions on using the HTTP server and the static site generator, including all supported environment variables and usage examples, check the [CLI documentation](docs/CLI.md). ## ๐Ÿ” How the server handles requests The three most important things to understand about how the server behaves - how it determines which error page to show, which format to return, and what request context it can expose. ### How the error code is resolved The server picks the HTTP status code from the **first** matching source: 1. Path: `/404`, `/404.html`, `/404.json`, `/503.xml`, etc. 2. `X-Code` request header 3. Default: `--default-error-page` (or `DEFAULT_ERROR_PAGE`, default: 404) ### How the response format is determined The response format is picked from the **first** matching source: 1. Path extension: `.html`, `.htm`, `.json`, `.xml`, `.txt` 2. `Content-Type` request header 3. `X-Format` request header (e.g. `X-Format: application/json`) 4. `Accept` request header 5. Default: **plain text** Supported formats: `HTML`, `JSON`, `XML`, `plain text`. ### Service endpoints The following HTTP endpoints can be used for health checks, monitoring, or other purposes: | Path | Description | |------------------------------------------------|------------------------------------------| | `/healthz`, `/health`, `/health/live`, `/live` | Liveness probe - always returns `200 OK` | | `/version` | Returns `{"version":"..."}` as JSON | ### Response headers Every error page response includes the following headers automatically: | Header | Value | Notes | |--------------------|-------------------------------------------|----------------------------------------------------| | `Content-Type` | e.g. `text/html; charset=utf-8` | Format-dependent | | `Content-Length` | Response body size in bytes | Always set | | `X-Robots-Tag` | `noindex, nofollow, nosnippet, noarchive` | Prevents error pages from being indexed | | `Retry-After` | `120` | Only for limited set of status codes | | `Content-Encoding` | `gzip` | Only when the client sends `Accept-Encoding: gzip` | Headers listed in `--proxy-headers` (default: `X-Request-Id`, `X-Trace-Id`, `X-Correlation-Id`, `X-Amzn-Trace-Id`) are copied from the incoming request to the response when present. ### HTTP status code of the response By default, the server always responds with **HTTP 200**, even when rendering error pages. This is the correct behavior when a reverse proxy (Nginx, Traefik, ingress-nginx) intercepts upstream error responses and replaces only the body - the proxy itself sends the original error status code back to the client. When error-pages is used as a **direct backend** - e.g. a catch-all route, a Kubernetes default backend, or standalone testing - it must return the correct status code itself. Enable `--send-same-http-code` (or env `SEND_SAME_HTTP_CODE=true`) to make the HTTP response status match the error code being rendered. ## ๐Ÿ“ Templating and Localization For detailed instructions on using custom templates and localization features, see the [templating documentation](docs/templating.md). ## ๐Ÿ”ง Development ### Requirements - [**Go 1.27+**](https://go.dev/doc/install) for building from source and running tests - Optional: [golangci-lint](https://golangci-lint.run/docs/welcome/install/local/) for linting - Optional: [docker](https://docs.docker.com/engine/install/debian/#install-using-the-convenience-script) for testing the Docker image locally - Optional: [helm](https://helm.sh/docs/intro/install/) + [kind](https://kind.sigs.k8s.io/docs/user/quick-start/) + docker for testing the Helm chart locally in Kubernetes - Optional: [helm-docs](https://github.com/norwoodj/helm-docs/releases/latest) for generating Helm chart documentation - Optional: [wrk](https://github.com/wg/wrk) for benchmarking the server performance - Optional: [watchexec](https://github.com/watchexec/watchexec/releases/latest) for live reloading the server during development **Commands**: ```shell go generate -skip readme ./... # (re)generate code, except docs go generate ./... # (re)generate everything go build ./cmd/error-pages/ && go build ./cmd/builder/ # build both binaries go test -race ./... # run all tests golangci-lint run --fix # lint the code and apply any available auto fixes helm-docs -c ./deploy/helm/ -t README.tpl.md -o README.md # regenerate chart readme file # run a live reloading server (useful for testing template changes) watchexec -r -- go run ./cmd/error-pages/ --show-details ``` ## ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ Contributors I want to say a big thank you to everyone who contributed to this project: [![contributors](https://contrib.rocks/image?repo=tarampampam/error-pages)][contributors] [contributors]:https://github.com/tarampampam/error-pages/graphs/contributors ## ๐Ÿ‘พ Support [![Issues][badge-issues]][issues] [![Issues][badge-prs]][prs] If you encounter any bugs in the project, please [create an issue][new-issue] in this repository. [badge-issues]:https://img.shields.io/github/issues/tarampampam/error-pages.svg?maxAge=45 [badge-prs]:https://img.shields.io/github/issues-pr/tarampampam/error-pages.svg?maxAge=45 [issues]:https://github.com/tarampampam/error-pages/issues [prs]:https://github.com/tarampampam/error-pages/pulls [new-issue]:https://github.com/tarampampam/error-pages/issues/new/choose ## ๐Ÿ“– License This is open-sourced software licensed under the [MIT License][license]. [license]:https://github.com/tarampampam/error-pages/blob/master/LICENSE