Quinto episodio della serie sul server casalingo. Finora abbiamo costruito la zona privata: la VPN WireGuard con wg-easy e, dietro, Vaultwarden con le tue password.
Prima o poi vuoi pubblicare qualcosa: un blog, una pagina personale, un servizio da condividere con chi non ha la tua VPN. A quel punto sembra che le strade siano due: affidarti a Cloudflare o a un hosting, oppure passare le serate a imparare la configurazione di Nginx a mano. C’è una terza via. Nginx Proxy Manager gira in Docker, fa da reverse proxy e ottiene i certificati Let’s Encrypt da un pannello web. Il tuo server avrà così un solo ingresso pubblico, controllato da te, separato dalla zona privata che resta dietro WireGuard.
Cos’è un reverse proxy e perché ti serve
Un reverse proxy è il portiere del palazzo. Da internet arrivano richieste per blog.tuodominio.it. Il portiere le riceve sulle porte 80 e 443, controlla il nome e le inoltra al container giusto, che invece non pubblica nessuna porta sull’host. Tutto il resto viene respinto.
Nginx Proxy Manager (NPM) mette questo portiere dietro un’interfaccia grafica. Dalla documentazione ufficiale: gestisce proxy host, redirect e host 404, emette certificati SSL gratuiti con Let’s Encrypt e offre access list, autenticazione HTTP, utenti con permessi e audit log. Il progetto è open source con licenza MIT e la versione documentata è la 2.16.0.
Usa tre porte:
- 80: HTTP, che serve anche per la validazione Let’s Encrypt;
- 443: HTTPS, il traffico pubblico vero e proprio;
- 81: il pannello di amministrazione.
Le prime due le apri sul router. La 81 non la apri mai.
Le tre regole prima di toccare il terminale
- Si pubblica solo ciò che è nato per essere pubblico. Un sito statico, un blog, una pagina di contatto. Vaultwarden, i pannelli di gestione e i backup restano dietro la VPN, come nell’episodio precedente.
- Il pannello admin non si espone. Sul router fai port forwarding di 80 e 443 verso il server e basta. Alla porta 81 arrivi dalla rete di casa o attraverso il tunnel WireGuard.
- I servizi pubblicati non aprono porte sull’host. La documentazione avanzata di NPM consiglia una rete Docker dedicata: NPM e i servizi parlano tra loro lì dentro, senza esporre porte su tutte le interfacce del server.
Ti servono anche un dominio e, se hai una connessione domestica con IP che cambia, un DNS dinamico: la guida di NPM lo cita tra i prerequisiti, insieme a Docker, Docker Compose e al port forwarding.
Installazione passo per passo
1. Crea la rete condivisa e la cartella di lavoro.
sudo docker network create proxy
mkdir -p ~/npm && cd ~/npm
nano docker-compose.yml
2. Incolla la configurazione. Parte dall’esempio ufficiale, con SQLite come database predefinito (sono supportati anche MySQL/MariaDB e PostgreSQL). Al posto di 192.168.1.50 metti l’IP locale del server: così la 81 risponde solo su quell’interfaccia, mai su un IP pubblico.
services:
app:
image: 'jc21/nginx-proxy-manager:2.16.0'
restart: unless-stopped
ports:
- '80:80'
- '443:443'
- '192.168.1.50:81:81'
environment:
TZ: "Europe/Rome"
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
default:
external: true
name: proxy
Hai un Raspberry Pi o un altro ARM a 32 bit? Dalla 2.14 armv7 non è più supportato: usa il tag 2.13.7. Sono supportati amd64 e arm64.
3. Avvia.
sudo docker compose up -d
Il primo avvio può richiedere un paio di minuti: NPM genera le chiavi, inizializza il database e crea l’utente amministratore.
4. Primo accesso. Da un dispositivo in casa o con la VPN attiva, apri http://192.168.1.50:81. Segui la procedura iniziale e imposta subito email e password tue, lunghe e uniche. Ovviamente vanno nel tuo Vaultwarden.
5. Metti online un servizio di prova. Crea una cartella separata (mkdir -p ~/sito/html && cd ~/sito) e un docker-compose.yml per un sito statico minimale, sulla stessa rete, senza la voce ports:
services:
sito:
image: nginx:alpine
restart: unless-stopped
volumes:
- ./html:/usr/share/nginx/html:ro
networks:
default:
external: true
name: proxy
6. Crea il proxy host. Nel pannello vai su Hosts → Proxy Hosts → Add Proxy Host. Come dominio metti blog.tuodominio.it, come hostname di destinazione sito (il nome del servizio Docker) e come porta 80. Nella scheda SSL richiedi un nuovo certificato Let’s Encrypt e attiva Force SSL.
HTTP-01 o DNS-01: scegli la sfida giusta
Per emettere un certificato, Let’s Encrypt deve verificare che il dominio sia tuo. Le due sfide principali funzionano in modo diverso:
- HTTP-01 funziona solo sulla porta 80 e non emette certificati wildcard. È la scelta più semplice se hai già aperto la 80.
- DNS-01 prova il controllo del dominio con un record TXT e richiede l’API del tuo provider DNS. In cambio supporta i wildcard (
*.tuodominio.it) e non richiede che il server sia raggiungibile da internet.
Il secondo punto conta più di quanto sembri. Con DNS-01 puoi dare un certificato valido anche a servizi solo interni, raggiungibili esclusivamente via VPN, senza aprirli al mondo. HTTPS vero anche nella zona privata, nessuna porta in più.
I certificati Let’s Encrypt durano 90 giorni e la raccomandazione è rinnovarli ogni 60. Esiste anche un profilo short-lived da 6 giorni. Le durate non sono modificabili.
Cosa puoi fare oggi: hardening e verifiche
Qualche accorgimento in più, tutto preso dalla documentazione avanzata di NPM:
- Access list: per un servizio che deve uscire da casa ma solo per pochi, aggiungi autenticazione HTTP o restrizioni per IP direttamente dal pannello.
- PUID/PGID: con
PUID: 1000ePGID: 1000i servizi interni non girano come root. La doc avverte che su alcuni sistemi può impedire l’apertura della porta 80: provalo e verifica. - Docker secrets: se passi a MySQL o PostgreSQL, non scrivere le password nel compose. Usa le variabili con suffisso
__FILEche puntano a un file segreto.
Poi verifica, da fuori e da dentro:
# Il certificato risponde ed è valido
curl -I https://blog.tuodominio.it
# Il servizio pubblicato non espone porte sull'host (output vuoto)
sudo docker compose -f ~/sito/docker-compose.yml port sito 80
# Dove ascolta la 81: deve comparire solo l'IP locale
sudo ss -tlnp | grep ':81'
Ultimo test, il più importante: dallo smartphone in 4G, con la VPN spenta, prova ad aprire http://TUO-IP-PUBBLICO:81. Deve fallire. Accendi WireGuard e il pannello risponde. Spegnilo e sparisce di nuovo. Se va così, le regole funzionano.
Conclusione
Il tuo server ora ha due zone separate. La porta pubblica è Nginx Proxy Manager su 80 e 443: certificati automatici, un nome per ogni servizio, niente porte sparse. La zona privata è dietro WireGuard, e lì restano password, pannelli e dati sensibili. Per mettere online un sito non hai dovuto chiedere il permesso a nessuno, né imparare a memoria la sintassi di Nginx. Nel prossimo episodio mettiamo al sicuro quello che abbiamo costruito: backup automatici e cifrati del server, perché un’infrastruttura che non sai ripristinare non è davvero tua.
Fonti:
- Nginx Proxy Manager - Guide — Nginx Proxy Manager
- Nginx Proxy Manager - Setup — Nginx Proxy Manager
- Nginx Proxy Manager - Advanced Configuration — Nginx Proxy Manager
- nginx-proxy-manager — GitHub / NginxProxyManager
- Challenge Types — Let’s Encrypt
- Let’s Encrypt FAQ — Let’s Encrypt
- WireGuard: fast, modern, secure VPN tunnel — WireGuard