Put IPP behind a reverse proxy
IPP listens on plain HTTP on port 3000 and expects a reverse proxy in front of it to terminate TLS and give it a public hostname. Any proxy will do. This page gives the minimal config for the common ones and the few things IPP needs from whichever you use.
What the proxy must do
- Terminate TLS and forward every request to the IPP container on port 3000. The gallery uses relative URLs, so no path rewriting is needed, and IPP serves from the root of its hostname or from
/shareand/son a domain it shares with Immich. - Match
PUBLIC_BASE_URL. SetPUBLIC_BASE_URLon the IPP container to the publichttps://address the proxy serves it on. IPP uses it for theog:imagetag that messaging apps read for link previews. Without it, IPP builds that URL from the request it receives, which behind a TLS-terminating proxy is plainhttp://. If you must leave it unset, make sure the proxy forwards the originalHostheader. - Allow large uploads only if you run the upload service. IPP itself needs no request size settings. See Request size limits.
In the examples, ipp-address:port is the address your reverse proxy reaches the IPP container on: the server's IP or hostname and the published port, 3000 with the default compose file.
Caddy
photos.example.com {
reverse_proxy ipp-address:port
}Caddy obtains the certificate and forwards the Host header on its own.
nginx
server {
listen 443 ssl;
server_name photos.example.com;
location / {
proxy_pass http://ipp-address:port;
proxy_set_header Host $host;
}
}Add your ssl_certificate lines as for any other site.
Traefik
A dynamic configuration file, in the same style as the mTLS guide:
http:
services:
ipp:
loadBalancer:
servers:
- url: "http://ipp-address:port"
routers:
ipp:
entryPoints:
- websecure
service: ipp
rule: "Host(`photos.example.com`)"
tls:
certResolver: letsencryptCloudflare
If Cloudflare proxies the domain, set the /share/video/*, /share/*/download and /s/*/download paths to Bypass Cache, otherwise video playback and zip downloads can fail. Cloudflare also refuses request bodies over 100 MB on the Free and Pro plans, which matters only for the upload service.
Check it
Visit https://photos.example.com/share/healthcheck. It returns ok when the proxy reaches IPP and IPP reaches Immich. If link previews in a messaging app show http:// or a private address, see Troubleshooting.
Other recipes
- Single domain with Immich: send only
/share/*and/s/*to IPP. - Let visitors send photos back: route the upload service as a path or its own hostname.
- Redirect root domain to a share.
- Custom error pages from the proxy.
- Securing Immich with mTLS: lock Immich itself down so only IPP is public.