Skip to content

Reverse proxy and HTTPS

Longbox speaks plain HTTP. To reach it from outside the house, or just to get HTTPS on the LAN, put a proxy such as Caddy in front and let it terminate TLS. deploy/proxy/ in the repository is a working setup for one host running several apps: a Caddyfile, the matching dnsmasq lines for the LAN, and a walkthrough from DNS records to a first check.

What Longbox itself needs, in /etc/longbox.env:

LONGBOX_HOST=127.0.0.1                        # only the proxy can reach it
LONGBOX_PUBLIC_URL=https://comics.example.com # what links are built from
LONGBOX_SECURE_COOKIES=1                      # session cookie over HTTPS only
  • LONGBOX_HOST keeps the port from answering to the network, so the proxy cannot be walked around.
  • LONGBOX_PUBLIC_URL is required, not cosmetic: password setup and reset links are built from it, and without it they take whatever Host header the caller sent.
  • LONGBOX_SECURE_COOKIES stops the session cookie travelling over plain HTTP. It also means signing in over http:// stops working, deliberately.

LONGBOX_TRUSTED_PROXIES is the setting that is easy to get wrong. The login throttle keys on the caller's address, and behind a proxy that address arrives in X-Forwarded-For, a header anyone can write. Longbox believes it only from the proxies listed here, and then only the rightmost entry that is not itself a proxy; reached any other way, the header is ignored and the connection's own address is used. The default is loopback, a proxy on the same host. A proxy in another container or on another machine has to be named (172.18.0.0/16, say), or every visitor looks like the proxy and shares one throttle. Never *, which believes the header from everybody.

OPDS readers send your password with every request, so point them only at the https:// address.

The working example

deploy/proxy/ in the repository holds a complete, commented setup for one host serving several applications behind a single Caddy:

File What it is
Caddyfile One site block per name, all on one :443 listener. Belongs at /etc/caddy/Caddyfile.
dnsmasq-lan-names.conf LAN answers for the same names, so devices at home reach the server directly. Belongs at /etc/dnsmasq.d/apps.conf.
README.md The walkthrough, from DNS records through router forwarding to a first check.

It configures the host rather than Longbox, which is why it sits beside the deployment files and is not installed by deploy/install.sh.

Three things it deliberately leaves out, each for a reason worth knowing:

  • No trusted_proxies in Caddy. Caddy is the outermost proxy, so it should believe nothing a client sends in X-Forwarded-*. By default it discards those headers and writes its own, and Longbox depends on that. Add it only if another proxy or a CDN sits in front.
  • No encode. Almost everything served is already compressed — comic pages, archives, audio — so it would spend CPU for nothing.
  • No Strict-Transport-Security yet. Browsers remember it for the period you set, so add it once everything has worked for a while, not while you are still testing.

Getting the certificate without burning the rate limit

Let's Encrypt allows only a handful of failed attempts an hour, and a typo in a DNS record or a missed router forward will exhaust them quickly.

Start on the staging service, which has generous limits and issues certificates browsers do not trust — fine for proving the plumbing:

{
    acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

Comment it out and reload once staging succeeds.

Editing the file is not enough on its own:

caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
sudo systemctl reload caddy

Reaching it from inside the house

Devices on the LAN should not have to go out to the public address and back in. Many routers cannot do that hairpin (NAT loopback) at all, and where they can it is slower and fails confusingly.

Resolving the same public names to the server's private address on the LAN is what makes one set of names, and one certificate each, work identically inside and outside the house.

One line per name — never address=/example.com/... for the whole domain, which would answer for every other name under it, mail and www included.

A note on ACME account email

Caddy's email option buys less than it looks like. Let's Encrypt ended expiration notification emails on 4 June 2025, so it no longer gets you a warning about a certificate that failed to renew. An address given now is not stored against the account at all.

Renewal monitoring, if you want it, has to come from outside — an external certificate monitor, or a cron job comparing openssl s_client ... | openssl x509 -enddate against today.