One day, you might want to replace the standard error pages of your HTTP server or K8S cluster with something more original and attractive. That's why this repository was created :) It contains: - A simple error page generator written in Go - Single-page error templates (themes) with various designs (located in the [templates][templates-dir] directory) that you can customize as you wish - A fast and lightweight HTTP server is available as a single binary file and Docker image. It includes built-in error page templates from this repository. You don't need anything except the compiled binary file or Docker image - Pre-generated error pages (sources can be [found here][preview-sources], and the [**demo** is always accessible here][preview-demo]) [preview-sources]:https://github.com/tarampampam/error-pages/tree/gh-pages [preview-demo]:https://tarampampam.github.io/error-pages/ [templates-dir]:https://github.com/tarampampam/error-pages/tree/master/templates ## ๐Ÿ”ฅ Features List - HTTP server written in Go, utilizing the extremely fast [FastHTTP][fasthttp] and in-memory caching - Respects the `Content-Type` HTTP header (and `X-Format`) value, responding with the corresponding format (supported formats: `json`, `xml`, and `plaintext`) - Error pages are configured to be excluded from search engine indexing (using meta tags and HTTP headers) to prevent SEO issues on your website - Logs written in `json` format - Contains a health check endpoint (`/healthz`) - Consumes very few resources and is suitable for use in resource-constrained environments - Lightweight Docker image, distroless, and uses an unprivileged user by default - [Go-template](https://pkg.go.dev/text/template) tags are allowed in the templates - Ready for integration with [Traefik][traefik], [Ingress-nginx][ingress-nginx], and more - Error pages can be embedded into your own Docker image with `nginx` in a few simple steps - Fully configurable - Distributed as a Docker image and compiled binary files - Localized HTML error pages (๐Ÿ‡บ๐Ÿ‡ธ, ๐Ÿ‡ซ๐Ÿ‡ท, ๐Ÿ‡บ๐Ÿ‡ฆ, ๐Ÿ‡ท๐Ÿ‡บ, ๐Ÿ‡ต๐Ÿ‡น, ๐Ÿ‡ณ๐Ÿ‡ฑ, ๐Ÿ‡ฉ๐Ÿ‡ช, ๐Ÿ‡ช๐Ÿ‡ธ, ๐Ÿ‡จ๐Ÿ‡ณ, ๐Ÿ‡ฎ๐Ÿ‡ฉ, ๐Ÿ‡ต๐Ÿ‡ฑ) - translation process [described here][l10n-dir] - other translations are welcome! [fasthttp]:https://github.com/valyala/fasthttp [traefik]:https://github.com/traefik/traefik [l10n-dir]:https://github.com/tarampampam/error-pages/tree/master/l10n ## ๐Ÿงฉ Install Download the latest binary file for your OS/architecture from the [releases page][latest-release] or use our Docker image: | Registry | Image | |-----------------------------------|-----------------------------------| | [GitHub Container Registry][ghcr] | `ghcr.io/tarampampam/error-pages` | | [Docker Hub][docker-hub] (mirror) | `tarampampam/error-pages` | > [!IMPORTANT] > Using the `latest` tag for the Docker image is highly discouraged due to potential backward-incompatible changes > during **major** upgrades. Please use tags in the `X.Y.Z` format. ๐Ÿ’ฃ **Or** you can also download the **already rendered** error pages pack as a [zip][pages-pack-zip] or [tar.gz][pages-pack-tar-gz] archive. [latest-release]:https://github.com/tarampampam/error-pages/releases/latest [docker-hub]:https://hub.docker.com/r/tarampampam/error-pages [ghcr]:https://github.com/tarampampam/error-pages/pkgs/container/error-pages [pages-pack-zip]:https://github.com/tarampampam/error-pages/zipball/gh-pages/ [pages-pack-tar-gz]:https://github.com/tarampampam/error-pages/tarball/gh-pages/ ## ๐Ÿช‚ Templates (themes) The following templates are built-in and available for use without any additional setup: > [!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.
Template Preview
app-down

used times
cats

used times
connection

used times
ghost

used times
hacker-terminal

used times
l7

used times
lost-in-space

used times
noise

used times
orient

used times
shuffle

used times
> [!NOTE] > The "used times" counter increments when someone start the server with the specified template. Stats service does > not collect any information about location, IP addresses, and so on. Moreover, the stats are open and available for > everyone at [error-pages.goatcounter.com](https://error-pages.goatcounter.com/). This is simply a counter to display > how often a particular template is used, nothing more. ## ๐Ÿ›  Usage scenarios ### HTTP server starting, utilizing either a binary file or Docker image First, ensure you have a precompiled binary file on your machine or have Docker/Podman installed. Next, start the server with the following command: ```bash $ ./error-pages serve # --- or --- $ docker run --rm -p '8080:8080/tcp' tarampampam/error-pages serve ``` That's it! The server will begin running and listen on address `0.0.0.0` and port `8080`. Access error pages using URLs like `http://127.0.0.1:8080/{page_code}.html`. To retrieve different error page codes using a static URL, use the `X-Code` HTTP header: ```bash $ curl -H 'X-Code: 500' http://127.0.0.1:8080/ ``` The server respects the `Content-Type` HTTP header (and `X-Format`), delivering responses in requested formats such as HTML, XML, JSON, and PlainText. Customization of these formats is possible via CLI flags or environment variables. For integration with [ingress-nginx][ingress-nginx] or debugging purposes, start the server with `--show-details` (or set the environment variable `SHOW_DETAILS=true`) to enrich error pages (including JSON and XML responses) with upstream proxy information. Switch themes using the `TEMPLATE_NAME` environment variable or the `--template-name` flag; available templates are detailed in the readme file below. > [!TIP] > Use the `--rotation-mode` flag or the `TEMPLATES_ROTATION_MODE` environment variable to automate theme > rotation. Available modes include `random-on-startup`, `random-on-each-request`, `random-hourly`, > and `random-daily`. To proxy HTTP headers from requests to responses, utilize the `--proxy-headers` flag or environment variable (comma-separated list of headers).
๐Ÿš€ Start the HTTP server with my custom template (theme) First, create your own template file, for example `my-super-theme.html`: ```html {{ code }}

YEAH! {{ message }}: {{ description }}

``` And simply start the server with the following command: ```bash $ docker run --rm \ -v "$(pwd)/my-super-theme.html:/opt/my-template.html:ro" \ -p '8080:8080/tcp' ghcr.io/tarampampam/error-pages:3 serve \ --add-template /opt/my-template.html \ --template-name my-template # --- or --- $ ./error-pages serve \ --add-template /opt/my-template.html \ --template-name my-template ``` And test it: ```bash $ curl -H "Accept: text/html" http://127.0.0.1:8080/503 503

YEAH! Service Unavailable: The server is temporarily overloading or down

```
๐Ÿš€ Generate a set of error pages using built-in or my own template Generating a set of error pages is straightforward. If you prefer to use your own template, start by crafting it. Create a file like this: ```html {{ code }}

{{ message }}: {{ description }}

``` Save it as `my-template.html` and use it as your custom template. Then, generate your error pages using the command: ```bash $ mkdir -p /path/to/output $ ./error-pages build --add-template /path/to/your/my-template.html --target-dir /path/to/output ``` This will create error pages based on your template in the specified output directory: ```bash $ cd /path/to/output && tree . โ”œโ”€โ”€ my-template โ”‚ โ”œโ”€โ”€ 400.html โ”‚ โ”œโ”€โ”€ 401.html โ”‚ โ”œโ”€โ”€ 403.html โ”‚ โ”œโ”€โ”€ 404.html โ”‚ โ”œโ”€โ”€ 405.html โ”‚ โ”œโ”€โ”€ 407.html โ”‚ โ”œโ”€โ”€ 408.html โ”‚ โ”œโ”€โ”€ 409.html โ”‚ โ”œโ”€โ”€ 410.html โ”‚ โ”œโ”€โ”€ 411.html โ”‚ โ”œโ”€โ”€ 412.html โ”‚ โ”œโ”€โ”€ 413.html โ”‚ โ”œโ”€โ”€ 416.html โ”‚ โ”œโ”€โ”€ 418.html โ”‚ โ”œโ”€โ”€ 429.html โ”‚ โ”œโ”€โ”€ 500.html โ”‚ โ”œโ”€โ”€ 502.html โ”‚ โ”œโ”€โ”€ 503.html โ”‚ โ”œโ”€โ”€ 504.html โ”‚ โ””โ”€โ”€ 505.html โ€ฆ $ cat my-template/403.html 403

Forbidden: Access is forbidden to the requested page

```
๐Ÿš€ Customize error pages within your own Nginx Docker image To create this cocktail, we need two components: - Nginx configuration file - A Dockerfile to build the image Let's start with the Nginx configuration file: ```nginx # File: nginx.conf server { listen 80; server_name localhost; error_page 401 /_error-pages/401.html; error_page 403 /_error-pages/403.html; error_page 404 /_error-pages/404.html; error_page 500 /_error-pages/500.html; error_page 502 /_error-pages/502.html; error_page 503 /_error-pages/503.html; location ^~ /_error-pages/ { internal; root /usr/share/nginx/errorpages; } location / { root /usr/share/nginx/html; index index.html index.htm; } } ``` And the Dockerfile: ```dockerfile FROM docker.io/library/nginx:1.27-alpine # override default Nginx configuration COPY --chown=nginx ./nginx.conf /etc/nginx/conf.d/default.conf # copy statically built error pages from the error-pages image # (instead of `ghost` you may use any other template) COPY --chown=nginx \ --from=ghcr.io/tarampampam/error-pages:3 \ /opt/html/ghost /usr/share/nginx/errorpages/_error-pages ``` Now, we can build the image: ```bash $ docker build --tag your-nginx:local -f ./Dockerfile . ``` And voilร ! Let's start the image and test if everything is working as expected: ```bash $ docker run --rm -p '8081:80/tcp' your-nginx:local $ curl http://127.0.0.1:8081/foobar | head -n 15 # in another terminal ```
๐Ÿš€ Usage with Traefik and local Docker Compose Instead of thousands of words, let's take a look at one compose file: ```yaml # file: compose.yml (or docker-compose.yml) services: traefik: image: docker.io/library/traefik:v3.1 command: #- --log.level=DEBUG - --api.dashboard=true # activate dashboard - --api.insecure=true # enable the API in insecure mode - --providers.docker=true # enable Docker backend with default settings - --providers.docker.exposedbydefault=false # do not expose containers by default - --entrypoints.web.address=:80 # --entrypoints..address for ports, 80 (i.e., name = web) ports: - "80:80/tcp" # HTTP (web) volumes: - /var/run/docker.sock:/var/run/docker.sock:ro labels: traefik.enable: true # dashboard traefik.http.routers.traefik.rule: Host(`traefik.localtest.me`) traefik.http.routers.traefik.service: api@internal traefik.http.routers.traefik.entrypoints: web traefik.http.routers.traefik.middlewares: error-pages-middleware depends_on: error-pages: {condition: service_healthy} error-pages: image: ghcr.io/tarampampam/error-pages:3 # using the latest tag is highly discouraged environment: TEMPLATE_NAME: l7 # set the error pages template labels: traefik.enable: true # use as "fallback" for any NON-registered services (with priority below normal) traefik.http.routers.error-pages-router.rule: HostRegexp(`.+`) traefik.http.routers.error-pages-router.priority: 10 # should say that all of your services work on https traefik.http.routers.error-pages-router.entrypoints: web traefik.http.routers.error-pages-router.middlewares: error-pages-middleware # "errors" middleware settings traefik.http.middlewares.error-pages-middleware.errors.status: 400-599 traefik.http.middlewares.error-pages-middleware.errors.service: error-pages-service traefik.http.middlewares.error-pages-middleware.errors.query: /{status}.html # define service properties traefik.http.services.error-pages-service.loadbalancer.server.port: 8080 nginx-or-any-another-service: image: docker.io/library/nginx:1.27-alpine labels: traefik.enable: true traefik.http.routers.test-service.rule: Host(`test.localtest.me`) traefik.http.routers.test-service.entrypoints: web traefik.http.routers.test-service.middlewares: error-pages-middleware ``` After executing `docker compose up` in the same directory as the `compose.yml` file, you can: - Open the Traefik dashboard [at `traefik.localtest.me`](http://traefik.localtest.me/dashboard/#/) - [View customized error pages on the Traefik dashboard](http://traefik.localtest.me/foobar404) - Open the nginx index page [at `test.localtest.me`](http://test.localtest.me/) - View customized error pages for non-existent [pages](http://test.localtest.me/404) and [domains](http://404.localtest.me/) Isn't this kind of magic? ๐Ÿ˜€
๐Ÿš€ Kubernetes (K8s) & Ingress Nginx Error-pages can be configured to work with the [ingress-nginx][ingress-nginx] helm chart in Kubernetes. - Set the `custom-http-errors` config value - Enable default backend - Set the default backend image ```yaml controller: config: custom-http-errors: >- 401,403,404,500,501,502,503 defaultBackend: enabled: true image: repository: ghcr.io/tarampampam/error-pages tag: '3' # using the latest tag is highly discouraged extraEnvs: - name: TEMPLATE_NAME # Optional: change the default theme value: l7 - name: SHOW_DETAILS # Optional: enables the output of additional information on error pages value: 'true' ```
## ๐Ÿฆพ Performance Hardware used: - 12th Gen Intelยฎ Coreโ„ข i7-1260P (16 cores) - 32 GiB RAM RPS: **~180k** ๐Ÿ”ฅ requests served without any errors, with peak memory usage ~60 MiB under the default configuration
Performance test details (click to expand) ```shell $ ulimit -aH | grep file core file size (blocks, -c) unlimited file size (blocks, -f) unlimited open files (-n) 1048576 file locks (-x) unlimited $ go build ./cmd/error-pages/ && ./error-pages --log-level warn serve $ ./error-pages perftest # in separate terminal Starting the test to bomb ONE PAGE (code). Please, be patient... Test completed successfully. Here is the output: Running 15s test @ http://127.0.0.1:8080/ 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 3.54ms 4.90ms 74.57ms 86.55% Req/Sec 16.47k 2.89k 38.11k 69.46% 2967567 requests in 15.09s, 44.70GB read Requests/sec: 196596.49 Transfer/sec: 2.96GB Starting the test to bomb DIFFERENT PAGES (codes). Please, be patient... Test completed successfully. Here is the output: Running 15s test @ http://127.0.0.1:8080/ 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 4.25ms 6.03ms 74.23ms 86.97% Req/Sec 14.29k 2.75k 32.16k 69.63% 2563245 requests in 15.07s, 38.47GB read Requests/sec: 170062.69 Transfer/sec: 2.55GB ```
## CLI interface Usage: ```bash $ error-pages [GLOBAL FLAGS] [COMMAND] [COMMAND FLAGS] [ARGUMENTS...] ``` Global flags: | Name | Description | Default value | Environment variables | |--------------------|---------------------------------------|:-------------:|:---------------------:| | `--log-level="โ€ฆ"` | Logging level (debug/info/warn/error) | `info` | `LOG_LEVEL` | | `--log-format="โ€ฆ"` | Logging format (console/json) | `console` | `LOG_FORMAT` | ### `serve` command (aliases: `s`, `server`, `http`) Please start the HTTP server to serve the error pages. You can configure various options - please RTFM :D. Usage: ```bash $ error-pages [GLOBAL FLAGS] serve [COMMAND FLAGS] [ARGUMENTS...] ``` The following flags are supported: | Name | Description | Default value | Environment variables | |------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------:|:---------------------------:| | `--listen="โ€ฆ"` (`-l`) | The HTTP server will listen on this IP (v4 or v6) address (set 127.0.0.1/::1 for localhost, 0.0.0.0 to listen on all interfaces, or specify a custom IP) | `0.0.0.0` | `LISTEN_ADDR` | | `--port="โ€ฆ"` (`-p`) | The TCP port number for the HTTP server to listen on (0-65535) | `8080` | `LISTEN_PORT` | | `--add-template="โ€ฆ"` | To add a new template, provide the path to the file using this flag (the filename without the extension will be used as the template name) | `[]` | *none* | | `--disable-template="โ€ฆ"` | Disable the specified template by its name (useful to disable the built-in templates and use only custom ones) | `[]` | *none* | | `--add-code="โ€ฆ"` | To add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes unless a more specific code is described previously) | `map[]` | *none* | | `--json-format="โ€ฆ"` | Override the default error page response in JSON format (Go templates are supported; the error page will use this template if the client requests JSON content type) | | `RESPONSE_JSON_FORMAT` | | `--xml-format="โ€ฆ"` | Override the default error page response in XML format (Go templates are supported; the error page will use this template if the client requests XML content type) | | `RESPONSE_XML_FORMAT` | | `--plaintext-format="โ€ฆ"` | Override the default error page response in plain text format (Go templates are supported; the error page will use this template if the client requests plain text content type or does not specify any) | | `RESPONSE_PLAINTEXT_FORMAT` | | `--template-name="โ€ฆ"` (`-t`) | Name of the template to use for rendering error pages (built-in templates: app-down, cats, connection, ghost, hacker-terminal, l7, lost-in-space, noise, orient, shuffle) | `app-down` | `TEMPLATE_NAME` | | `--disable-l10n` | Disable localization of error pages (if the template supports localization) | `false` | `DISABLE_L10N` | | `--default-error-page="โ€ฆ"` | The code of the default (index page, when a code is not specified) error page to render | `404` | `DEFAULT_ERROR_PAGE` | | `--send-same-http-code` | The HTTP response should have the same status code as the requested error page (by default, every response with an error page will have a status code of 200) | `false` | `SEND_SAME_HTTP_CODE` | | `--show-details` | Show request details in the error page response (if supported by the template) | `false` | `SHOW_DETAILS` | | `--proxy-headers="โ€ฆ"` | HTTP headers listed here will be proxied from the original request to the error page response (comma-separated list) | `X-Request-Id,X-Trace-Id,X-Amzn-Trace-Id` | `PROXY_HTTP_HEADERS` | | `--rotation-mode="โ€ฆ"` | Templates automatic rotation mode (disabled/random-on-startup/random-on-each-request/random-hourly/random-daily) | `disabled` | `TEMPLATES_ROTATION_MODE` | | `--read-buffer-size="โ€ฆ"` | Per-connection buffer size in bytes for reading requests, this also limits the maximum header size (increase this buffer if your clients send multi-KB Request URIs and/or multi-KB headers (e.g., large cookies), note that increasing this value will increase memory consumption) | `5120` | `READ_BUFFER_SIZE` | ### `build` command (aliases: `b`) Build the static error pages and put them into a specified directory. Usage: ```bash $ error-pages [GLOBAL FLAGS] build [COMMAND FLAGS] [ARGUMENTS...] ``` The following flags are supported: | Name | Description | Default value | Environment variables | |---------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------:|:---------------------:| | `--add-template="โ€ฆ"` | To add a new template, provide the path to the file using this flag (the filename without the extension will be used as the template name) | `[]` | *none* | | `--disable-template="โ€ฆ"` | Disable the specified template by its name (useful to disable the built-in templates and use only custom ones) | `[]` | *none* | | `--add-code="โ€ฆ"` | To add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes unless a more specific code is described previously) | `map[]` | *none* | | `--disable-l10n` | Disable localization of error pages (if the template supports localization) | `false` | `DISABLE_L10N` | | `--index` (`-i`) | Generate index.html file with links to all error pages | `false` | *none* | | `--target-dir="โ€ฆ"` (`--out`, `--dir`, `-o`) | Directory to put the built error pages into | `.` | *none* | ### `healthcheck` command (aliases: `chk`, `health`, `check`) Health checker for the HTTP server. The use case - docker health check. Usage: ```bash $ error-pages [GLOBAL FLAGS] healthcheck [COMMAND FLAGS] [ARGUMENTS...] ``` The following flags are supported: | Name | Description | Default value | Environment variables | |---------------------|-----------------------------------------------|:-------------:|:---------------------:| | `--port="โ€ฆ"` (`-p`) | TCP port number with the HTTP server to check | `8080` | `LISTEN_PORT` | ## ๐Ÿฆพ 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 [ingress-nginx]:https://github.com/kubernetes/ingress-nginx/tree/main/charts/ingress-nginx