> [!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]
[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