Aller au contenu
selfhostr

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:8181, 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:

  1. DNS does not point here yet. Check again with dig.
  2. 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.
  3. 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:

  1. 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.
  2. 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