SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-10-03

Cloudflare Tunnel senza porte aperte: guida VPS

Configura un tunnel nominato con credenziali, regole ingress e servizio systemd; poi chiudi 80 e 443 e limita l'app a localhost per evitare accessi diretti.

Cosa fa Cloudflare Tunnel e cosa significa davvero non avere porte aperte

Cloudflare Tunnel installa sul VPS un piccolo daemon chiamato cloudflared. Il daemon apre una connessione in uscita verso Cloudflare e la mantiene attiva. Le richieste per il tuo hostname arrivano all'edge di Cloudflare e vengono inoltrate attraverso quella connessione già esistente. Di conseguenza, il server non deve accettare connessioni in ingresso.

Il passaggio che la maggior parte delle guide non considera: installare il tunnel non chiude alcuna porta. Se le porte 80 e 443 sono ancora aperte nel firewall e l'applicazione continua ad ascoltare su 0.0.0.0, hai aggiunto un secondo punto di accesso invece di sostituire il primo. L'IP dell'origin è ancora raggiungibile. Chiunque lo individui può bypassare direttamente Cloudflare. La chiusura di queste porte è un'operazione manuale. È anche il passaggio che rende utili tutte le attività precedenti.

cloudflared deve poter raggiungere in uscita region1.v2.argotunnel.com e region2.v2.argotunnel.com sulla porta 7844. Utilizza UDP per il protocollo QUIC e passa a TCP per HTTP/2. In una rete con filtraggio del traffico in uscita, consenti entrambi i protocolli oppure forza il percorso TCP con --protocol http2.

Prima di iniziare

  • Un dominio già presente in un account Cloudflare, con i nameserver di Cloudflare che gestiscono la zona. cloudflared tunnel route dns scrive i record in questa zona, quindi la zona deve esistere.
  • Accesso in uscita sulla porta 7844 dal VPS, tramite UDP e TCP.
  • Un'applicazione già in ascolto localmente, anche se per il primo test è soltanto python3 -m http.server 8080.
  • sudo sul server e una seconda sessione SSH già aperta prima di modificare il firewall.

Installare cloudflared su Ubuntu o Debian

Cloudflare associa un pacchetto .deb a ogni release cloudflared, quindi l'installazione richiede un solo download e una sola chiamata dpkg.

curl -fsSL -o /tmp/cloudflared.deb \
  https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i /tmp/cloudflared.deb
cloudflared --version

Eseguire prima dpkg --print-architecture se non si è certi dell'architettura del sistema. Su ARM a 64 bit il nome del file termina con arm64 invece di amd64 e non cambia altro. La stampa di una stringa di versione da parte di cloudflared --version è l'unica conferma necessaria prima di procedere.

Un pacchetto installato in questo modo non rientra nel percorso di aggiornamento di apt. Di conseguenza, apt-get upgrade non lo aggiornerà mai e gli aggiornamenti diventano a carico dell'amministratore. sudo cloudflared update scarica la release più recente e sostituisce il binario in uso. Quando il servizio esiste già, eseguire poi sudo systemctl restart cloudflared in modo che il processo in esecuzione utilizzi il nuovo binario. Inserire questa attività nella stessa pianificazione degli altri aggiornamenti, perché un demone tunnel è un software esposto a Internet anche se non apre alcuna porta.

Accedere e creare un tunnel con nome

cloudflared tunnel login

Su un VPS headless non si apre alcun browser. Copiate quindi l'URL visualizzato nel browser del laptop e selezionate la zona. Al termine, esiste ~/.cloudflared/cert.pem.

cert.pem è la credenziale del vostro account. Autorizza la creazione di tunnel, la scrittura di record DNS nella zona e l'eliminazione dei tunnel. Il tunnel in esecuzione non la utilizza mai. Trattatela come una password, perché una copia di quel singolo file è sufficiente per consentire a qualcuno di pubblicare nuovi hostname nel vostro dominio.

cloudflared tunnel create homelab

Un'esecuzione completata correttamente stampa entrambe le righe riportate di seguito. L'UUID contenuto nelle righe è il valore da inserire nel file di configurazione.

Tunnel credentials written to /home/you/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.
Created tunnel homelab with id 6ff42ae2-765d-4adf-8112-31c55c1551ef

Quel file JSON identifica il tunnel ed è l'unica credenziale necessaria al servizio in esecuzione. Chiunque ne sia in possesso può registrarsi come il vostro tunnel e ricevere il vostro traffico. Non è possibile ruotarlo autonomamente: revocarlo significa cloudflared tunnel delete homelab e creare un nuovo tunnel.

Conservare il file delle credenziali in un percorso appropriato

Il servizio viene eseguito come root. Inserisci quindi il file in una directory di proprietà di root, invece di lasciarlo in una home directory che potrebbe essere accessibile a un processo di backup o a un account di accesso condiviso.

sudo install -d -m 700 -o root -g root /etc/cloudflared
sudo install -m 600 -o root -g root \
  ~/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json \
  /etc/cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
rm ~/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
sudo ls -l /etc/cloudflared
cloudflared tunnel list

ls -l dovrebbe mostrare -rw------- root root sul file JSON. cloudflared tunnel list legge cert.pem, quindi continua a funzionare e dovrebbe visualizzare il nome del tunnel, il relativo UUID e il numero di connessioni attualmente attive.

Scrivere config.yml con regole di ingresso reali

Scrivere la configurazione in /etc/cloudflared/config.yml, non nella directory home. Il motivo è il seguente. cloudflared service install copia la configurazione che trova in /etc/cloudflared/config.yml e poi inserisce in modo statico --config /etc/cloudflared/config.yml nell'unità systemd. Se si crea il file in ~/.cloudflared/config.yml, la copia è una fotografia acquisita una sola volta. Ogni modifica successiva alla copia nella home non cambia nulla: il servizio continua a usare le vecchie regole e non viene generato alcun avviso. Creare il file direttamente nella posizione prevista elimina completamente il problema.

tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /etc/cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
loglevel: info

ingress:
  - hostname: app.example.com
    service: http://127.0.0.1:8080
  - hostname: files.example.com
    service: http://127.0.0.1:8081
  - hostname: grafana.example.com
    path: ^/api/
    service: http://127.0.0.1:3000
  - service: http_status:404

Le regole vengono lette dall'alto verso il basso e viene applicata la prima corrispondenza. Una regola senza hostname corrisponde a qualsiasi hostname, per questo la regola generica deve essere per ultima. Se la si omette, la configurazione viene rifiutata con The last ingress rule must match all URLs (i.e. it should not have a hostname or path filter). http_status:404 è un servizio integrato che risponde con 404 e non esegue altre operazioni. È necessario: senza di esso, una richiesta diretta a un hostname che non si intende pubblicare ricadrebbe nella prima regola reale che si trova per ultima.

Usare 127.0.0.1 nell'URL service: anziché localhost. In Ubuntu, localhost risolve prima ::1 e un'applicazione in ascolto soltanto sul loopback IPv4 rifiuta quella connessione. La riga di log è dial tcp [::1]:8080: connect: connection refused e il client riceve una risposta 502.

In questo caso, http:// senza TLS è corretto perché la connessione interna non lascia la macchina. Usare https:// solo quando l'applicazione locale richiede TLS (sicurezza del livello di trasporto) e prevedere x509: certificate is valid for example.com, not localhost quando il certificato non corrisponde al nome utilizzato nella connessione. Correggere il problema con originServerName in originRequest oppure accettare il rischio con noTLSVerify: true.

Verificare le regole prima di avviare qualsiasi servizio:

sudo cloudflared --config /etc/cloudflared/config.yml tunnel ingress validate
sudo cloudflared --config /etc/cloudflared/config.yml tunnel ingress rule https://app.example.com/login

ingress validate segnala che la configurazione è valida oppure indica la regola che ha causato l'errore. ingress rule riceve un URL e stampa la prima regola che vi corrisponde. È il modo più rapido per verificare che un'espressione regolare path non corrisponda a ciò che si presumeva.

Puntare il DNS al tunnel

cloudflared tunnel route dns homelab app.example.com
cloudflared tunnel route dns homelab files.example.com
cloudflared tunnel route dns homelab grafana.example.com

Ogni comando crea un record CNAME con proxy attivo che punta a 6ff42ae2-765d-4adf-8112-31c55c1551ef.cfargotunnel.com. Questa destinazione viene risolta solo all'interno della rete Cloudflare. Di conseguenza, la risposta DNS pubblica per il nome host contiene un indirizzo Cloudflare e l'indirizzo IP del VPS non viene mai pubblicato. Un wildcard hostname in config.yml richiede comunque un record DNS corrispondente per ogni nome effettivamente utilizzato.

Se esiste già un record, il comando restituisce un errore di questo tipo:

Failed to add route: code: 1003, reason: Failed to create record app.example.com with err An A, AAAA, or CNAME record with that host already exists.

Il record esistente è quasi sempre il vecchio record A che punta all'indirizzo IP pubblico del VPS. È proprio il record da rimuovere. Eliminalo dal dashboard di Cloudflare, quindi esegui di nuovo il comando. Se lo lasci, il DNS continua a pubblicare l'indirizzo IP dell'origine e il tunnel non nasconde nulla.

Installarlo come servizio per mantenerlo attivo dopo un riavvio

Eseguilo una volta in primo piano, perché un errore è molto più facile da leggere nel terminale locale che nel journal.

sudo cloudflared --config /etc/cloudflared/config.yml tunnel run homelab

Un avvio corretto registra diverse righe Registered tunnel connection, una per ogni edge location, ciascuna con il proprio connIndex. Apri uno dei tuoi hostname in un browser e verifica che le regole di ingresso conducano alla destinazione prevista, quindi arrestalo con Ctrl-C.

sudo cloudflared --config /etc/cloudflared/config.yml service install
systemctl status cloudflared

Questo scrive /etc/systemd/system/cloudflared.service insieme a cloudflared-update.service e cloudflared-update.timer, quindi esegue systemctl enable cloudflared.service e systemctl start cloudflared.service al posto tuo. enable è la parte importante in questo caso, perché consente di riavviare il tunnel dopo un reboot. Il ExecStart dell'unità è cloudflared --no-autoupdate --config /etc/cloudflared/config.yml tunnel run, perciò il percorso della configurazione non è modificabile.

In questo passaggio possono verificarsi tre errori con messaggi precisi. possible conflicting configuration in /home/you/.cloudflared/config.yml and /etc/cloudflared/config.yml indica che entrambi i file esistono e cloudflared rifiuta di scegliere: elimina quello che non vuoi usare. configuration file ... must contain entries for the tunnel to run and its associated credentials (tunnel: TUNNEL-UUID, credentials-file: CREDENTIALS-FILE) indica che la configurazione usa la forma abbreviata quick url: invece delle chiavi del tunnel denominato; questa forma non può essere eseguita come servizio. cloudflared service is already installed indica che un'unità precedente è ancora presente, quindi esegui prima sudo cloudflared service uninstall.

Non esiste un reload. Dopo aver modificato /etc/cloudflared/config.yml, esegui sudo systemctl restart cloudflared. Poi verifica il comportamento dopo il reboot invece di darlo per scontato:

sudo reboot
# once it is back
systemctl is-enabled cloudflared
systemctl is-active cloudflared
journalctl -u cloudflared -n 50 --no-pager
curl -sI https://app.example.com

Il fatto che is-enabled stampi enabled e is-active stampi active è l'obiettivo principale di questa sezione. Una volta create le route e avviato il servizio, cert.pem non ha più alcun compito sul server: rm ~/.cloudflared/cert.pem. Per aggiungere in seguito un hostname è sufficiente eseguire di nuovo cloudflared tunnel login.

Chiudi le porte 80 e 443, altrimenti il tunnel è soltanto un percorso aggiuntivo

Servono due modifiche, ed entrambe sono necessarie. Se ne applichi una senza l'altra, l'origine resta raggiungibile.

Per prima cosa, associa l'app all'indirizzo loopback. In nginx questo significa usare listen 127.0.0.1:8080; al posto di listen 80;, come mostrato nella spiegazione di questa configurazione nginx per il reverse proxy. In Docker Compose significa usare ports: - "127.0.0.1:8080:80". La forma semplice "8080:80" pubblica il servizio su tutte le interfacce e Docker crea regole NAT (network address translation) proprie. I pacchetti le attraversano prima che ufw possa esaminarli, quindi una regola di blocco in ufw non sarà sufficiente. Questo caso è trattato nella guida perché le porte pubblicate da Docker ignorano ufw.

sudo ss -lntp

Ogni servizio spostato dovrebbe ora mostrare 127.0.0.1:8080 nella colonna Local Address. Una riga con 0.0.0.0:8080 o *:8080 indica che il servizio è ancora in ascolto verso Internet.

Secondo: chiudi le porte.

sudo ufw status numbered
sudo ufw delete allow 'Nginx Full'
sudo ufw status

Elimina le regole che consentono il traffico sulle porte 80 e 443 invece di aggiungere regole di blocco sopra quelle esistenti. ufw si ferma alla prima regola corrispondente e una vecchia regola di autorizzazione più in alto nell'elenco avrebbe la precedenza. Mantieni la regola SSH. La guida di base al firewall ufw illustra il resto di questo set di regole. La maggior parte dei provider VPS gestisce anche un firewall di rete separato nel pannello di controllo. Non è ufw, quindi chiudi le porte 80 e 443 anche lì.

Ora verifica da un altro sistema, perché curl http://127.0.0.1:8080 eseguito direttamente sul server non dimostra nulla sulla raggiungibilità dall'esterno.

# run these from a different machine
nc -vz 203.0.113.10 443
curl -sI https://app.example.com

Un nc rifiutato o scaduto verso l'indirizzo IP pubblico, insieme a un 200 tramite il nome host, è il risultato atteso. La guida come verificare se una porta è realmente aperta descrive altri metodi di test.

Il tunnel fornisce il trasporto, non l'autenticazione. Qualsiasi servizio pubblicato tramite il tunnel è raggiungibile pubblicamente se non aggiungi un accesso autenticato, ad esempio Cloudflare Access sul perimetro oppure un proxy OAuth2 davanti all'app sul server. Anche SSH richiede una configurazione separata, perché il tunnel non lo protegge: mantieni aperta la porta 22, ma limita l'accesso ai tuoi indirizzi sorgente.

Vantaggi e costi di Cloudflare Tunnel

I vantaggi sono concreti. L'indirizzo IP dell'origine non viene più pubblicato, nessuna porta in ingresso viene esposta, la configurazione funziona anche da una macchina priva di IP pubblico, il certificato pubblico è gestito da Cloudflare e quindi sul server non deve essere eseguito alcun client ACME (automatic certificate management environment), mentre gli attacchi volumetrici vengono assorbiti ai margini della rete Cloudflare invece di consumare la banda disponibile.

Anche i costi sono concreti. Cloudflare termina TLS ai propri edge: la richiesta del visitatore viene decrittografata in quel punto e nuovamente crittografata nel tunnel, quindi Cloudflare può leggere il traffico. Questo consente di usare il firewall, la cache e le regole Access di Cloudflare; nessuna impostazione può disattivare questa possibilità finché si utilizza il loro proxy. Se non è accettabile che una terza parte disponga dei dati in chiaro, fermati qui e scegli un'altra soluzione.

Cloudflare diventa inoltre una dipendenza essenziale per la raggiungibilità. Quando cloudflared non è connesso, i visitatori ricevono la pagina di errore 1033 di Cloudflare invece della tua applicazione. Hai quindi rimosso deliberatamente il percorso diretto che avrebbero potuto usare come alternativa.

Da un browser normale, verso un hostname pubblico arrivano soltanto HTTP, HTTPS e WebSocket. Qualsiasi altro protocollo TCP, come SSH o RDP (remote desktop protocol), oppure un game server, richiede software anche sul lato client: cloudflared access tcp per inoltrare una porta locale oppure il client WARP. Per questi protocolli non esiste un percorso utilizzabile senza client.

Le dimensioni dei corpi delle richieste sono limitate all'edge. Un upload oltre il limite viene rifiutato con HTTP 413 prima di raggiungere l'applicazione. Ad agosto 2026 il limite è 100 MB nei piani Free e Pro e maggiore nei piani a pagamento. Verifica quindi la pagina dei limiti attuali di Cloudflare prima di progettare la soluzione sulla base di un valore specifico. I termini self-service di Cloudflare limitano inoltre l'uso del proxy principalmente per distribuire video e altri file non HTML di grandi dimensioni. Leggili prima di indirizzare una libreria multimediale verso un tunnel gratuito.

Cloudflare Tunnel, reverse SSH tunnel o Tailscale Funnel

Tutti e tre funzionano solo con connessioni in uscita, quindi possono essere usati da un server senza porte in ingresso e senza un IP pubblico. La differenza riguarda chi può accedere ai dati in chiaro e quale hostname vede il pubblico.

Un reverse SSH tunnel richiede una seconda macchina con un IP pubblico. Questa macchina diventa il punto di ingresso: certificato, reverse proxy e firewall sono tutti a tuo carico. Nessun altro decritta il traffico. La configurazione include più componenti e richiede autossh oppure un'unità systemd con Restart=always per sopravvivere a una breve interruzione della rete. La procedura per configurare un reverse SSH tunnel con CGNAT illustra questa configurazione.

Tailscale Funnel è il confronto più diretto. Funziona solo con connessioni in uscita e TLS termina sulla tua macchina, quindi i relay di Tailscale non vedono i dati in chiaro. Il compromesso riguarda i nomi e le porte: Funnel pubblica solo nomi sotto il dominio ts.net della tua tailnet e soltanto sulle porte 443, 8443 e 10000. La differenza tra Tailscale Serve e Funnel descrive entrambi gli aspetti.

Scegli quindi in base al vincolo che si applica realmente al tuo caso. Scegli Cloudflare Tunnel quando il pubblico deve raggiungere il tuo dominio e accetti che Cloudflare possa leggere il traffico. Scegli Tailscale Funnel quando un hostname ts.net è accettabile e non vuoi affidare i dati in chiaro a un proxy. Scegli un reverse SSH tunnel quando possiedi già un server pubblico e non vuoi che alcun soggetto terzo si trovi nel percorso.

FAQ

Devo mantenere aperta la porta 443 con Cloudflare Tunnel?

No. cloudflared stabilisce una connessione in uscita verso Cloudflare sulla porta 7844 e ogni richiesta torna attraverso quella connessione, quindi non viene utilizzata alcuna porta in ingresso. Tuttavia, l'installazione del tunnel non chiude automaticamente le porte. Elimina le regole che consentono 80 e 443 in ufw, chiudile nel firewall di rete separato del provider, associa l'applicazione a 127.0.0.1 ed elimina ogni record A residuo che continua a pubblicare l'indirizzo IP del VPS. Verifica con sudo ss -lntp sul server e con nc -vz <your-ip> 443 da un'altra macchina.

Perché il mio hostname mostra l'errore Cloudflare 1033?

L'errore 1033 indica che Cloudflare gestisce il record DNS per quell'hostname, ma non trova alcun cloudflared integro e connesso che possa ricevere la richiesta. Il processo potrebbe essere arrestato oppure potrebbe essere in esecuzione senza riuscire a raggiungere Cloudflare. Controlla systemctl status cloudflared e journalctl -u cloudflared -n 50, quindi verifica che la porta in uscita 7844 sia consentita sia per UDP sia per TCP. Un firewall che blocca UDP e QUIC senza consentire il fallback TCP produce esattamente questo errore. cloudflared tunnel info homelab mostra le connessioni attualmente rilevate da Cloudflare; un elenco vuoto indica che il problema è sul tuo lato.

Perché ricevo un errore 502 Bad Gateway attraverso il tunnel?

Un errore 502 indica che cloudflared è stato raggiunto, ma non è riuscito a raggiungere il servizio locale. Il problema si trova quindi tra questi due endpoint, non in Cloudflare. Leggi il log. dial tcp [::1]:8080: connect: connection refused indica che non è in ascolto alcun processo all'indirizzo specificato. Il [::1] presente in quel messaggio indica di solito che hai scritto localhost nell'URL service:, mentre l'applicazione è associata solo a IPv4. In questo caso, scrivi invece http://127.0.0.1:8080. HTTP/1.x transport connection broken: malformed HTTP response indica il caso opposto: hai scritto https:// per un'origine che usa HTTP non cifrato.

Posso eseguire SSH, RDP o un game server tramite Cloudflare Tunnel?

Non con un client standard. Un hostname pubblico attraverso un tunnel supporta HTTP, HTTPS e WebSocket, cioè i protocolli utilizzati da un browser. Qualsiasi altro protocollo TCP richiede software anche sulla macchina client: cloudflared access tcp per inoltrare una porta locale oppure il client WARP. Se vuoi usare SSH da qualsiasi macchina senza installare nulla, il tunnel non è lo strumento adatto. Mantieni aperta la porta 22 e limita l'accesso in base all'indirizzo sorgente.

Cloudflare può vedere il mio traffico attraverso il tunnel?

Sì. Cloudflare termina TLS sul proprio edge, decritta la richiesta in quel punto e la cifra nuovamente prima di inviarla attraverso il tunnel al server. Questa decrittazione consente al firewall, alla cache e alle policy Access di Cloudflare di funzionare, ma significa anche che i dati in chiaro sono presenti sui suoi sistemi. Non esiste una configurazione che lo eviti mentre utilizzi il loro proxy. Se questo non è accettabile, usa Tailscale Funnel oppure esegui un reverse proxy di tua proprietà su un server pubblico.

#cloudflare-tunnel#cloudflared#zero-trust#firewall#self-hosting