guide · 5 min de lecture
Setting up a reverse proxy
Readable names and HTTPS instead of six ports to remember: Nginx Proxy Manager, Let's Encrypt, and the “Internal Error” waiting for everyone.
By your third service, you will hit a problem nobody warns you about.
Jellyfin listens on 8096, Immich on 2283, Vaultwarden on 8080. You remember
them at first. Then you add a fourth, you get a port wrong, and you go digging
through docker-compose.yml to find which was which. And when someone else in
the house asks for the photos address, you read out 192.168.1.42:2283 —
twice, because the first attempt did not work.
Worse: all of it travels in clear text. A password manager reached over HTTP on your own network is still a password manager handing out its session unencrypted.
A reverse proxy fixes both at once. One entry point, readable names, HTTPS everywhere:
photos.example.com → 192.168.1.42:2283
movies.example.com → 192.168.1.42:8096
vault.example.com → 192.168.1.42:8080
This guide uses Nginx Proxy Manager: it is nginx with a web interface on top. Let's Encrypt certificates take three clicks and renew themselves. Budget thirty minutes the first time.
What you need
- Ubuntu Server with Docker — see Installing Docker.
- A domain name. It is the one unavoidable cost in this guide: roughly ten euros a year. Without a domain there is no Let's Encrypt certificate, and so no HTTPS.
- A way to reach your server from outside, if that is what you want: a Cloudflare tunnel or a forwarded port.
And if you want to expose nothing at all, this guide still pays off: readable names and HTTPS on your own network is most of the comfort already.
Step 1 — Free up ports 80 and 443
The proxy has to be alone on them. If something already holds them, move it first:
sudo ss -tlnp | grep -E ':(80|443)\s'
No output means nothing is listening and you can carry on. An apache2 or
nginx line means a web server came along with something else, and it has to
go (sudo systemctl disable --now apache2).
Step 2 — Start the proxy
In a directory of its own, a docker-compose.yml:
services:
npm:
image: jc21/nginx-proxy-manager:2.15.1
restart: unless-stopped
ports:
- '80:80' # public HTTP
- '443:443' # public HTTPS
- '81:81' # admin interface
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
docker compose up -d
The version is pinned, and that is not a detail: latest on the component
carrying all your traffic means a surprise update can take every service down
at once. Updating your containers covers
when and how to move up a version.
Step 3 — First login
Go to http://192.168.1.42:81 — 81, not 80. Port 80 serves public
traffic; the admin interface lives beside it.
The first account is in the documentation, so everyone knows it:
[email protected]
changeme
The interface asks you to replace it immediately. Actually do it — this admin page can issue a certificate for any subdomain and point any service anywhere.
Step 4 — Point your domain here
At your registrar (or in Cloudflare, if the domain is delegated there), create one record per service:
| Type | Name | Value |
|---|---|---|
| A | photos |
your public IP |
| A | movies |
your public IP |
Or, simpler, a wildcard: a * record sending every subdomain to the same
address. You will never come back here to add a service again.
If you go through a Cloudflare tunnel, the tunnel's own records do this job and your public IP never appears.
Check it took effect before going further — DNS propagation takes anywhere from minutes to hours:
dig +short photos.example.com
An address means you are good. Nothing at all means wait: asking for a certificate now would fail without the error saying why.
Step 5 — Your first host
Under Hosts → Proxy Hosts → Add Proxy Host:
- Domain Names:
photos.example.com - Scheme:
http - Forward Hostname / IP: the server's local IP
- Forward Port:
2283 - Block Common Exploits: ticked
Then the SSL tab:
- SSL Certificate: Request a new SSL Certificate
- Force SSL: ticked — otherwise the service stays reachable in clear text
- HTTP/2 Support: ticked
- Your email address, and accept the Let's Encrypt terms
Save. Ten seconds later https://photos.example.com answers, with a padlock.
The error coming your way: "Internal Error"
That is what Nginx Proxy Manager says when Let's Encrypt could not verify the domain. It never says why. The three causes, most common first:
- DNS does not point here yet. Check again with
dig. - Port 80 does not reach the proxy. Let's Encrypt validates by connecting on port 80 from outside — even if you only ever intend to serve HTTPS. Without that port forwarded, validation fails.
- Your ISP blocks port 80. This is common. Switch to DNS validation: in the SSL tab, tick Use a DNS Challenge and pick your provider. No port is needed, and it is the only way to get a wildcard certificate.
Local only, exposing nothing
You can have names and HTTPS without a single packet coming in:
- Buy a real domain, but point its records at the server's private IP
(
192.168.1.42). This is allowed, and invisible from outside. - Use DNS validation for the certificate: Let's Encrypt then never needs to reach your machine.
The result: https://photos.example.com works at home and nowhere else. That
is often exactly what you wanted.
What it does not do
A reverse proxy is not authentication. It encrypts transport and tidies up addresses; it stops nobody from opening the page. What protects a service is its own password — and not exposing it at all when nothing requires it.
Do not put the proxy's own interface (port 81) behind a public subdomain. It is the key to everything else.
Next
Vidéo
Lawrence Systems walks through Nginx Proxy Manager and its SSL handling — worth watching if you would rather see the interface before committing to it.
Rien n'est envoyé à YouTube tant que vous n'avez pas lancé la lecture. Ce site ne dépose aucun cookie qui lui soit propre.Regarder sur YouTube