Reverse proxy and HTTPS¶
The server speaks plain HTTP only, on port 8080 inside the container. It has no TLS support, no HTTP-to-HTTPS redirect and no HSTS. For HTTPS, put a reverse proxy in front of it and let the proxy handle certificates.
The canonical Compose file already prepares for this. It publishes the port on 127.0.0.1 only, so the server is unreachable from the network except through a proxy running on the same host. (The Unraid template and Compose file publish port 6266 on all interfaces instead; point your proxy at that.)
Requirements for the proxy¶
- Give MangaPixer its own hostname (or its own port). Serving it under a sub-path such as
https://example.lan/mangapixer/is not supported: the app expects to live at/. - Pass the original
Hostheader through, and sendX-Forwarded-ForandX-Forwarded-Proto. The server uses them for activation links, secure cookies and sign-in rate limiting (see What the server sees behind a proxy). Caddy sends all three by default. - Allow uploads up to 512 MiB (the server's default restore limit) if you want to restore backups through the proxy (see Backup and restore).
- Allow slow responses. The first page of an archive on a drive that has to spin up can take a minute or more. Page requests stay open until the page is ready.
- No WebSocket or streaming configuration is needed; everything is ordinary HTTP requests.
Caddy¶
Caddy obtains and renews certificates by itself, redirects HTTP to HTTPS, passes the Host and X-Forwarded-* headers through, and has no upload limit or short read timeout by default. For a name on your LAN that the public internet cannot reach, use Caddy's internal certificate authority:
comics.home.arpa {
tls internal
reverse_proxy 127.0.0.1:8080
}
With a real public domain whose DNS points at the proxy, drop the tls internal line and Caddy uses Let's Encrypt instead. With tls internal, each device needs to trust Caddy's root certificate once; Caddy's documentation explains how.
nginx¶
server {
listen 80;
server_name comics.example.lan;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name comics.example.lan;
ssl_certificate /etc/ssl/comics.example.lan.crt;
ssl_certificate_key /etc/ssl/comics.example.lan.key;
client_max_body_size 512m; # backup restore uploads
proxy_read_timeout 300s; # first pages from sleeping drives
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
The three proxy_set_header lines matter. Without Host, nginx sends 127.0.0.1:8080 as the host and activation links point there. Without the X-Forwarded-* lines, the server thinks every request is plain HTTP from the proxy itself (see below).
Use the http2 on; directive only with nginx 1.25.1 or later. On older versions, write listen 443 ssl http2; instead.
What the server sees behind a proxy¶
The server reads the X-Forwarded-For, X-Forwarded-Proto and X-Forwarded-Host headers, but only from a trusted proxy. By default it trusts connections from loopback (127.0.0.0/8, ::1) and the private IPv4 ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). That covers a proxy on the same machine, on a Docker network, or elsewhere on your LAN. Headers from any other address are ignored, so a client on the internet cannot pretend to be someone else.
With a trusted proxy that sends these headers:
- Cookies are marked
Securewhen the original request was HTTPS, so the browser never sends them over plain HTTP. They are alsoHttpOnlyandSameSite=Strict. Plain-HTTP access on your LAN keeps working with non-secure cookies. - Sign-in rate limiting sees each client's real address, so one person mistyping a password does not lock out everyone behind the proxy.
- Activation links use your public address and
https://.
If your proxy connects from a public or other untrusted address (for example a proxy on another server reaching MangaPixer over the internet or a VPN range), add it with MangaPixer__Network__KnownProxies or MangaPixer__Network__KnownNetworks. Only one proxy in front of MangaPixer is supported; with two chained proxies, the server sees the address of the one in front of it.
If the proxy does not send the headers, every request looks like plain HTTP from the proxy: cookies are not marked Secure, the per-IP sign-in limit (10 failures in 5 minutes) is shared by everyone, and activation links start with http://.
Whatever you set up, you can add HSTS at the proxy for extra safety, for example header Strict-Transport-Security "max-age=31536000" in Caddy. Logs never contain client IP addresses; see Troubleshooting.
Activation links¶
When an admin creates a user without a password, the activation link is built from the address the admin's browser used. Behind a proxy that sends the headers above, it starts with your public https:// address.
- If the link starts with
http://, your proxy is not sendingX-Forwarded-Proto, or it is not trusted (see above). Changehttp://tohttps://before you send it, or fix the proxy. - If the link shows
127.0.0.1or another internal address, your proxy is not passing theHostheader (see the nginx note above).
Exposing the server to the internet¶
The server is built for a home network: a small number of known users, accounts created by an admin, and no public sign-up. If you still make it reachable from the internet:
- Always use HTTPS via a proxy, as shown above.
- Keep the container's port bound to loopback (the canonical default) so the proxy is the only way in.
- Use strong passwords, and keep the admin account for administration.
- Keep the server updated.