klemek 7f7cfb7d3f
Python Lint CI / ruff-format-check (push) Successful in 1m59s
Python Lint CI / ruff (push) Successful in 2m2s
Docker CI / docker-build (push) Failing after 4m34s
Python Lint CI / ty (push) Successful in 2m30s
Python Test CI / coverage (push) Successful in 2m50s
chore: version 1.3.3
2026-05-09 12:26:38 +02:00
2026-04-13 00:13:03 +02:00
2026-05-03 19:02:46 +02:00
2026-04-17 00:48:44 +02:00
2026-04-11 17:58:39 +02:00
2026-05-04 22:53:05 +02:00
2026-04-21 22:51:35 +02:00
2026-04-25 19:11:25 +02:00
2026-04-12 23:52:48 +02:00
2026-05-09 12:26:38 +02:00
2026-05-09 12:26:38 +02:00

Stapler

Static pages as simple as a gzip file

logo.svg

Table of Contents

What is Stapler

Stapler is a static page delivery server.

You feed it a tar.gz file of your content, and it serves it at your desired path or host.

It handles:

  • static files delivery: /path/xxx
  • directory roots translation into index.html (if existing)
  • host translation to specific path: my.example.com => /path/
  • auto host discovery and certificate issuing (via certbot)
  • redirect pages: /path-2/ => (redirect) https://my-website.com
  • proxy pages: /path-3/ => https://my-website.com
  • updating/deleting content via API with curl

Python CLI

Basic usage

Theres 3 basic commands for the stapler server:

  • run: launch the server to serve at the desired paths (see full CLI help for env vars and such)
  • renew: renew all existing certificates (the server must be running in the background)
  • token: generate a new secure API token

See Docker for a quick deployable server.

Full CLI Help
usage: stapler [-h] [--debug | --no-debug] [-d DATA_DIR] [--certificates | --no-certificates] [--self-signed-path SELF_SIGNED_PATH] [--certbot | --no-certbot] [--certbot-conf CERTBOT_CONF] [--certbot-www CERTBOT_WWW] [--host HOST]
               [--http-port HTTP_PORT] [--https-port HTTPS_PORT] [--https | --no-https] [-t TOKEN_SALT] [--max-size-bytes MAX_SIZE] [-b BIND]
               COMMAND ...

Static pages as simple as a gzip file

positional arguments:
  COMMAND
    run                 Run Stapler server
    renew               Renew certificates
    token               Generate a new token

options:
  -h, --help            show this help message and exit
  --debug, --no-debug
  -d, --data-dir DATA_DIR
                        directory where pages are/will be stored (default: ./data)
  --certificates, --no-certificates
                        Handle certificates (default: true)
  --self-signed-path SELF_SIGNED_PATH
                        Self-signed certificates dir (default: ./data/.certificates)
  --certbot, --no-certbot
                        Use Certbot (default: true)
  --certbot-conf CERTBOT_CONF
                        Certbot config dir (default: /etc/letsencrypt)
  --certbot-www CERTBOT_WWW
                        Certbot www dir (default: ./data/.certbot)
  --host HOST           server default host (default: localhost)
  --http-port HTTP_PORT
                        server http port (default: 80)
  --https-port HTTPS_PORT
                        server https port (default: 443)
  --https, --no-https   Use https (implies --certificates) (default: true)
  -t, --token-salt TOKEN_SALT
                        salt for tokens generation
  --max-size-bytes MAX_SIZE
                        max size of accepted archives (in bytes) (default: 2000000)
  -b, --bind BIND       server bind address (default: 0.0.0.0)

(Each option can be supplied with equivalent environment variable.)

HTTP API

Create/update page from gzip

PUT /{page}/
  X-Token (your API token)
  X-Host (optional host as entrypoint)
  X-Host-Only (optional host as entrypoint)
  X-SPA (optional SPA file)
  (body with tar data)
# create archive from 'dist' dir and upload it to /my-project/
tar -czC dist -f dist.tar.gz .
curl -X PUT \
  --fail-with-body \
  -H 'X-Token: <TOKEN>' \
  --data-binary "@dist.tar.gz" \
  https://stapler-host/my-project/

# same thing but one-liner
tar -czC dist . | curl -X PUT \
  --fail-with-body \
  -H 'X-Token: <TOKEN>' \
  --data-binary @- \
  https://stapler-host/my-project/

# make stapler server identify myproject.example.com and /my-project/
tar -czC dist . | curl -X PUT \
  --fail-with-body \
  --data-binary @- \
  -H 'X-Token: <TOKEN>' \
  -H 'X-Host: myproject.example.com' \
  https://stapler-host/my-project/

# make stapler server identifiers myproject.example.com only
tar -czC dist . | curl -X PUT \
  --fail-with-body \
  --data-binary @- \
  -H 'X-Token: <TOKEN>' \
  -H 'X-Host-Only: myproject.example.com' \
  https://stapler-host/my-project/

# make a SPA site at /my-project/index.html
tar -czC dist . | curl -X PUT \
  --fail-with-body \
  --data-binary @- \
  -H 'X-Token: <TOKEN>' \
  -H 'X-SPA: index.html' \
  https://stapler-host/my-project/

Note

Creating/updating comes with X-Host for enabling host-based resolving but you can also use X-Host-Only to do the same thing but disable listing as /path/.

Create/update page with redirect

PUT /{page}/
  X-Token (your API token)
  X-Redirect (redirection target)
  X-Host (optional host as entrypoint)
  X-Host-Only (optional host as entrypoint)
# create /my-project/ that redirects to https://github.com/my-project
curl -X PUT \
  --fail-with-body \
  -H 'X-Token: <TOKEN>' \
  -H 'X-Redirect: https://github.com/my-project' \
  https://stapler-host/my-project/

# simple redirect from root host to www
curl -X PUT \
  --fail-with-body \
  -H 'X-Token: <TOKEN>' \
  -H 'X-Proxy: https://www.my-website.com' \
  -H 'X-Host: my-website.com' \
  https://stapler-host/my-website-www/

Create/update page with proxy

PUT /{page}/
  X-Token (your API token)
  X-Proxy (proxy target)
  X-Host (optional host as entrypoint)
  X-Host-Only (optional host as entrypoint)
# create /my-website/ that proxies to http://host.containers.internal:8000
curl -X PUT \
  --fail-with-body \
  -H 'X-Token: <TOKEN>' \
  -H 'X-Proxy: http://host.containers.internal:8000' \
  https://stapler-host/my-project/

Delete page

DELETE /{page}/
  X-Token (your API token)
# delete /my-project/
curl -X DELETE \
  --fail-with-body \
  -H 'X-Token: <TOKEN>' \
  https://stapler-host/my-project/

Publishing your site to your stapler server

Sample github actions

  deploy:
    name: Deploy
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v8
        with:
          name: production-files
          path: ./dist
      - name: Create archive
        run: tar -czC dist -f dist.tar.gz .
      - name: Deploy to Stapler server
        run: |
          curl -X PUT --fail-with-body --data-binary "@dist.tar.gz" \
            -H 'X-Token: ${{ secrets.STAPLER_TOKEN }}' \
            -H 'X-Host: ${{ vars.TARGET_HOST }}' \
            ${{ vars.STAPLER_URL }}

Redirecting hosts with DNS

Root host (e.g. example.com)

name type value
@ A (server ipv4)
@ AAAA (server ipv6)

Subdomain (e.g. www.example.com)

name type value
(subdomain) CNAME (server host).

Docker

Stapler ships with a deploy-ready Dockerfile and a sample docker compose stack with:

  • a main server exposing on port 80 (http) and port 443 (https)
  • a weekly crontab for updating certificates
cp crontab.example crontab
cp compose.example.yml compose.yml
cp .env.example .env
$EDITOR .env # update HOST and TOKEN_SALT
docker compose up
# whenever you need a new token
docker compose run --rm stapler token
S
Description
No description provided
Readme 614 KiB
Stapler 1.4.0 Latest
2026-05-11 17:26:59 +02:00
Languages
Python 97.8%
Makefile 1.9%
Dockerfile 0.3%