La tua app funziona. Hai avviato il server di sviluppo, aperto http://localhost:3000, e fa quello che deve fare. Poi qualcuno ti chiede un link, e scopri che l'URL sul tuo schermo non significa nulla per nessuno tranne te.
Ci sono tre modi per dare a un'app localhost un URL pubblico senza un VPS, più un'opzione più rapida quando l'altra persona è sulla tua rete locale, e scegliere tra loro non è una questione di strumenti. È una questione di quanto a lungo la cosa deve restare raggiungibile, e di se può girare da qualche parte che non sia il tuo laptop. Qui sotto trovi ogni percorso, il comando che ti dà un URL, e la cosa esatta che farà smettere di funzionare quell'URL.
TL;DR
- Qualcuno sul tuo Wi-Fi deve vederla: fai ascoltare il server di sviluppo su tutte le interfacce di rete e passa il tuo IP di LAN. Pronto in pochi secondi, morto nel momento in cui il visitatore lascia la tua rete.
- Ti serve un link che chiunque possa aprire, per la prossima ora: avvia un tunnel (
cloudflared, ngrok, localtunnel, localhost.run). URL HTTPS pubblico in circa un minuto, nessuna modifica al router, e muore insieme al processo che l'ha avviato. - Deve restare attiva con il laptop chiuso: porta l'app su un piano di hosting gratuito. Questo la scollega dal tuo laptop e introduce nuove regole su carte di credito, uso commerciale e sopravvivenza dei dati a un riavvio.
- La tua app non ha bisogno di codice server al momento della richiesta: compilala e metti l'output statico su un host statico. Può restare online senza il tuo laptop e non ha alcun processo applicativo da risvegliare, finché l'account e i limiti d'uso dell'host lo consentono.
- Un'impostazione predefinita che vale la pena conoscere:
next deve i pianipython -m http.serverascoltano già su ogni interfaccia di rete senza alcun flag. Se davi per scontato che il tuo server di sviluppo fosse privato e confinato al tuo laptop, non lo è.
Quale percorso è adatto alla tua app
Tre di questi quattro percorsi danno alla tua app un URL pubblico su internet; il primo raggiunge solo la tua rete, il che lo rende allo stesso tempo il più veloce e il più limitato. Ordinali per quanto a lungo l'URL deve sopravvivere e la scelta si fa quasi da sola.
| Percorso | Tempo per avere un URL | Quanto dura | Cosa la uccide | Per chi è |
|---|---|---|---|---|
| Stessa rete | Secondi | Finché siete entrambi sulla rete | Il visitatore passa a un altro Wi-Fi | Un collega alla scrivania accanto, o il tuo stesso telefono |
| Tunnel | Circa un minuto | Finché il processo è in esecuzione | Chiudere il laptop, terminare il terminale, raggiungere i limiti del piano | Una demo, una presentazione a un cliente, un test di webhook |
| Piano di hosting gratuito | Da 10 a 30 minuti | A tempo indeterminato, con condizioni | Sospensione per inattività, un filesystem effimero o i termini del piano | Qualcosa che deve rispondere mentre dormi |
| Build statica | Da 10 a 20 minuti | A tempo indeterminato | Aver bisogno di codice lato server al momento della richiesta | App che possono essere generate interamente in fase di build o girare lato client |
Quali righe sono disponibili per te dipende da tre cose che puoi verificare dentro il tuo progetto:
- L'app ha bisogno di eseguire il tuo codice server al momento della richiesta? Una route Flask o FastAPI, un endpoint
server.js, o una logica server specifica per richiesta richiede un host lato server. Il codice server eseguito in fase di build non esclude automaticamente un deploy statico: i Server Components di Next.js possono girare durantenext build, e iGETRoute Handlers statici possono essere prerenderizzati. Se ogni richiesta a runtime può essere servita come asset statici o inviata direttamente dal browser a un'API esterna, il percorso statico resta aperto. - Legge o scrive un file che deve conservare? Un file di database (
.db,.sqlite), una cartella di upload, un file JSON che modifica. Se sì, controlla il modello di storage dell'host prima del deploy. I web service gratuiti di Render e le istanze gratuite di Koyeb usano storage locale effimero, mentre le Vercel Functions hanno un filesystem in sola lettura con uno spazio/tmptemporaneo. Metti lo stato persistente in un volume durevole, un database o uno storage a oggetti invece di dare per scontato che il disco locale dell'app sopravviva. - Ha bisogno di un segreto a runtime? Una chiave in un file
.envfunziona così com'è nei primi due percorsi, perché l'app gira ancora sulla tua macchina. Negli altri due la reinserisci nelle impostazioni d'ambiente del provider, e non deve trovarsi nel repository che pubblichi.
Condividila sulla tua rete
next dev ascolta già su ogni interfaccia di rete della tua macchina (è tutto ciò che significa 0.0.0.0 quando lo vedi), e lo stesso fa python -m http.server. Nessuno dei due ha bisogno di un flag, quindi il server di sviluppo che hai in esecuzione in questo momento è probabilmente già raggiungibile dal tuo telefono sullo stesso Wi-Fi.
Next.js documenta -H come il modo per cambiare quell'hostname, con un valore predefinito di 0.0.0.0, e la documentazione di Python dice che il modulo si lega a tutte le interfacce a meno che tu non passi --bind 127.0.0.1. Questo lo rende il modo più veloce per condividere un'app localhost: nessun account, nessuna installazione, nessun deploy. Agli altri server di sviluppo comuni bisogna dirlo esplicitamente.
# Already listening on all interfaces. Nothing to add.
next dev
python -m http.server 8000
streamlit run app.py
# Needs the flag.
npm run dev -- --host # Vite
flask run --host=0.0.0.0
uvicorn main:app --host 0.0.0.0 # FastAPI
La documentazione di Vite: server.host ha come valore predefinito localhost, e accetta --host da riga di comando oppure server.host: '0.0.0.0' nel file di configurazione. Uvicorn usa come predefinito 127.0.0.1, il che copre FastAPI, dato che è ciò che lo esegue. Streamlit lascia server.address non impostato, e il suo riferimento di configurazione precisa che impostarlo limita l'accesso a quel solo indirizzo: non impostato significa senza restrizioni.
Poi ti serve l'indirizzo da passare. È l'IP della tua macchina sulla rete locale, non localhost:
# macOS
ipconfig getifaddr en0
# Linux
hostname -I
# Windows (PowerShell)
ipconfig | findstr IPv4
Dai al tuo visitatore http://<that-address>:3000 ed è dentro. Il problema è la forma dell'intero percorso: quell'indirizzo non significa nulla fuori dalla tua rete. Nell'istante in cui passa a un altro Wi-Fi, alla rete cellulare o a casa sua, il link per lui è morto.
Nota: se il comando gira bene e l'altro dispositivo continua a non connettersi, quasi sempre è il firewall del sistema operativo, non il comando. La documentazione del firewall di Apple dice che macOS mostra un avviso per un'app che non hai ancora autorizzato e nega la connessione finché non intervieni. Windows invece chiede quale profilo di rete si applica, mantenendo regole separate per le reti private e pubbliche . Scegli Privata su una rete domestica o d'ufficio. Mai Pubblica.
Mettila dietro un tunnel
Un tunnel è un piccolo programma che gira accanto alla tua app e le dà un indirizzo HTTPS pubblico. Un solo comando ti dà un tunnel gratuito verso localhost in circa un minuto, e vale la pena sapere da subito che l'URL muore nell'istante in cui quel comando termina:
cloudflared tunnel --url http://localhost:3000
Sul tuo router non cambia nulla, per via della direzione in cui viaggia la connessione. La tua macchina apre una connessione in uscita verso l'edge del provider, dello stesso tipo che il tuo browser apre per caricare qualsiasi pagina, e il provider la tiene aperta e vi spinge dentro le richieste in entrata. Le porte in entrata dal tuo lato restano chiuse, ed è per questo che funziona sul Wi-Fi di un hotel, su un hotspot del telefono e su una connessione domestica il cui router non controlli.
Nota: quest'ultimo caso merita una verifica di sessanta secondi prima di pensare invece al port forwarding. Apri la pagina di stato del router, trova l'IP WAN che riporta e confrontalo con il tuo vero IP pubblico preso da un qualsiasi servizio «qual è il mio IP». Se l'IP WAN rientra in
100.64.0.0/10, la causa probabile è il CGNAT. Se l'IP WAN e l'IP pubblico semplicemente differiscono, sai che c'è un altro livello NAT a monte, ma potrebbe essere CGNAT o un normale doppio NAT. In entrambi i casi, il port forwarding solo su questo router potrebbe non bastare. Quell'intervallo è riservato dalla RFC 6598 come spazio di indirizzi condiviso, cioè quello dietro cui ti mette il tuo ISP quando finisce gli indirizzi.
Le opzioni differiscono soprattutto per ciò che ti chiedono per prime.
I quick tunnel di Cloudflare sono il comando qui sopra: nessun account, nessun dominio, un sottodominio casuale su trycloudflare.com . Cloudflare li limita a 200 richieste in corso, restituendo 429 oltre quella soglia, non supporta i Server-Sent Events, e nella stessa documentazione dice che i tunnel gratuiti sono per test e sviluppo, non per pubblicare un sito web in produzione.
ngrok richiede prima una registrazione, poi ngrok http 3000. Il piano gratuito attuale ti dà 5 $ di utilizzo incluso una tantum che non si rinnova ogni mese, fino a 3 endpoint online, 1 GB di traffico, 20.000 richieste HTTP/S e una pagina di avviso intermedia che il visitatore deve cliccare per proseguire. Ottieni anche un dominio di sviluppo gratuito assegnato automaticamente, che ngrok ha annunciato nel 2023 per chiudere la vecchia lamentela dell'URL che cambiava a ogni riavvio.
localtunnel non richiede registrazione né installazione oltre a npx: npx localtunnel --port 3000. Ottieni un sottodominio casuale, e il README è esplicito sul fatto che --subdomain richiede un nome senza garantirlo.
localhost.run non installa nulla, perché usa il client SSH già incluso nel tuo sistema operativo: ssh -R 80:localhost:3000 localhost.run. La sua documentazione precisa che non è necessario alcun download e che non serve creare alcun account per i domini gratuiti.
VS Code ce l'ha nel pannello Ports, comodo se vivi già dentro l'editor. Richiede l'accesso con GitHub o Microsoft, e l'impostazione predefinita ti farà inciampare: una porta inoltrata è Privata, il che significa che al visitatore viene chiesto di accedere con il tuo account finché non imposti la porta su Pubblica. (Va bene per un collega. Inutile per il cliente che vuole solo cliccare un link.)
Tailscale Funnel fa la stessa cosa, con due vincoli che di solito decidono la questione: l'URL può vivere solo sul dominio del tuo tailnet, e può soltanto ascoltare sulle porte 443, 8443 e 10000.
Qualunque cosa tu scelga, sii consapevole di cosa hai distribuito. Con un tunnel pubblico senza controlli d'accesso, tutto ciò che il server di sviluppo serve è raggiungibile da chiunque abbia quell'URL, comprese le route che non hai mai linkato e qualsiasi interfaccia di debug lasciata attiva. Va bene per una demo di quindici minuti. Molto meno per un URL incollato in un Discord pubblico.
La scadenza ti coglie di sorpresa perché può sembrare che l'app si sia rotta. La maggior parte delle vie rapide qui dipende ancora da un software di tunnel in esecuzione sul tuo laptop: ferma cloudflared, ngrok, localtunnel o la sessione SSH di localhost.run e l'inoltro si interrompe. Tailscale Funnel è l'eccezione quando lo avvii con --bg, che mantiene la configurazione di Funnel attiva in background e la ripristina dopo un riavvio. Nessuno di questi può servire la tua app locale mentre il laptop stesso è offline. Puoi lasciare un tunnel in esecuzione per giorni, e funzionerà fino a quando chiudi il coperchio o raggiungi il limite di richieste.
Un tunnel è lo strumento giusto per una demo e quello sbagliato per l'hosting: il suo uptime è l'uptime del tuo laptop.
Lascia che l'app esca dalla tua macchina
Questo è il primo percorso in cui il tuo laptop smette di essere indispensabile, e il primo in cui i termini contano più degli strumenti. Ciò che lo interrompe qui non è un orologio. È una sospensione per inattività, un reset del filesystem, o un piano che decide che la tua app non è il genere di cosa che vuole su un'istanza gratuita.
Le opzioni qui sotto vanno da piani gratuiti permanenti a brevi periodi di prova. Alcune possono tenere un'app online a tempo indeterminato entro i loro limiti; altre si fermano dopo una prova a durata fissa o richiedono un account a pagamento per il calcolo. Controlla le regole su fatturazione, sospensione e storage prima del deploy.
I termini dei piani gratuiti qui sotto sono stati verificati sulla pagina dei prezzi o della documentazione di ciascun provider il 7 settembre 2026.
| Fornitore | Uso commerciale? | I dati sopravvivono a un riavvio? | Il problema |
|---|---|---|---|
| Netlify | Consentito | Sì, con Netlify Blobs o Database | Nessuna carta per iniziare; esaurire tutti i crediti mensili del piano Free mette in pausa i progetti fino al ciclo di fatturazione successivo, a meno di un upgrade |
| Render | Non dichiarato | Persi al riavvio | Nessuna carta per iniziare; va in sospensione dopo 15 minuti di inattività; il Postgres gratuito scade 30 giorni dopo la creazione |
| Cloudflare Pages / Workers | Non dichiarato | Sì, con KV, D1, R2 o Durable Objects | 500 build Pages al mese; il piano gratuito Workers è limitato a 100.000 richieste al giorno |
| Vercel | Non su Hobby | Persi al riavvio | Hobby è solo per uso personale; il filesystem delle funzioni è in sola lettura |
| GitHub Pages | Non consentito | Solo output statico | Niente attività online, e-commerce o SaaS commerciale; timeout di deploy di 10 minuti |
| PythonAnywhere | Non dichiarato | Sì | Gli account gratuiti raggiungono solo una allowlist di host esterni; l'app web gratuita scade dopo un mese se non rinnovata |
| Fly.io | Non dichiarato | Sì, con un Fly Volume | Nessun piano gratuito permanente: 2 ore-macchina o 7 giorni; le Machine di prova si fermano automaticamente dopo 5 minuti di esecuzione, e la prova include 20 GB di storage su volume; le app si fermano alla fine della prova finché non aggiungi una carta |
| Koyeb | Non dichiarato | Nessuno storage locale durevole | Carta obbligatoria; Koyeb effettua e annulla una pre-autorizzazione di 29 $, ma la registrazione seleziona Pro per impostazione predefinita e ne addebita il costo proporzionale, a meno che tu non passi a Starter |
| Hugging Face Spaces | Non dichiarato | Persi al riavvio | Gli Static Spaces sono gratuiti; Gradio e Docker richiedono in genere un piano a pagamento, ma gli account personali gratuiti idonei possono ospitare fino a due Spaces Gradio su ZeroGPU |
| Railway | Non dichiarato | Sì, se l'app usa il volume da 0,5 GB incluso | Prova di 30 giorni con 5 $ di credito una tantum, poi un piano Free da 0 $ con 1 $ di credito risorse al mese; nessuna carta richiesta |
«Non indicato» significa che le pagine del provider non rispondono alla domanda per il loro piano gratuito. Trattalo come un'incognita, non come un sì o un no.
Tre di quelle righe meritano uno sguardo in più prima di iniziare. Fly.io non ha un piano gratuito permanente: la prova termina dopo 2 ore-macchina o 7 giorni. Koyeb include un'istanza gratuita, ma il suo flusso di registrazione e fatturazione richiede attenzione prima del deploy. Hugging Face mantiene gratuiti gli Static Spaces, mentre i nuovi Spaces Gradio e Docker richiedono un account a pagamento, salvo la limitata eccezione di ZeroGPU. Railway non appartiene più a questa lista di avvertimenti: dopo la prova di 30 giorni con 5 $ di credito, ora passa a un piano Free da 0 $ con 1 $ di credito risorse al mese.
Poi c'è la questione dei dati, dove un'app funzionante diventa silenziosamente un'app rotta. I web service gratuiti di Render girano su un filesystem effimero, e la documentazione è brutale: tutto ciò che vi viene scritto, immagini caricate e database SQLite locali esplicitamente inclusi, va perso a ogni nuovo deploy, riavvio e sospensione. Le funzioni di Vercel girano su un filesystem in sola lettura con solo uno spazio temporaneo, quindi neanche un file SQLite scritto dalla tua app è al sicuro lì. Se la tua app tiene lo stato in un file, spostalo su uno storage durevole: un database gestito, uno storage a oggetti o un volume persistente dove la piattaforma ne offre uno.
La sospensione per inattività merita di essere provata prima di impegnarsi. Sul piano gratuito di Render, 15 minuti di inattività mettono il servizio a dormire, e la richiesta successiva lo risveglia in circa un minuto, mostrando una pagina di caricamento a chi ha cliccato il tuo link. Per un pezzo da portfolio, un'alzata di spalle. Per un cliente che apre il link durante una call, sessanta pessimi secondi.
PythonAnywhere ha una versione più sottile della trappola. Non c'è sospensione per inattività, ma un'app web gratuita ha una scadenza di un mese e si ferma a meno che tu non clicchi il link di rinnovo che PythonAnywhere ti invia via email, e gli account gratuiti hanno un accesso internet in uscita limitato e raggiungono solo una allowlist di host esterni. Un'app che chiama un'API fuori da quella lista resta perfettamente online e fallisce ogni richiesta verso quell'API non autorizzata (un modo orribile di fare debug, visto che nulla sembra fuori uso da nessuna parte).
Pubblicala come sito statico
Se nulla di ciò che fa la tua app richiede codice server al momento della richiesta, un host statico è l'opzione più vicina al «configura e dimentica»: può restare online senza il tuo laptop, e non c'è alcun processo applicativo in attesa di risvegliarsi. L'account e i limiti d'uso dell'host valgono comunque. Più app sono idonee di quanto ti aspetteresti, ed è per questo che vale la pena rispondere alla prima di quelle tre domande prima di dare per scontato di aver bisogno di un host lato server.
La regola è più stretta di «ha un backend?». La tua app è idonea se ogni richiesta che fa va o ai tuoi file statici o direttamente dal browser all'API di qualcun altro. Interrogare Supabase o un'API pubblica dal browser va bene, con l'unica avvertenza che una chiave nel codice del browser è una chiave che hai reso pubblica. Ciò che chiude la porta è la necessità che il tuo codice giri su un server a ogni richiesta.
Se è idonea, il procedimento è breve:
npm run build # Vite writes to dist/, a Next.js static export writes to out/
Poi pubblica quell'output di build su un host statico. Cloudflare Pages, Netlify e Vercel possono compilare da un repository collegato. GitHub Pages può pubblicare file statici da un branch o usare GitHub Actions per eseguire la build del tuo framework e distribuire l'output generato.
GitHub Pages ha vincoli abbastanza netti da contare fin da subito. Solo statico, un solo sito utente o organizzazione per account, e limiti d'uso che dichiarano che non è hosting gratuito per un'azienda, un sito e-commerce o un SaaS commerciale. Se la tua app dovrà accettare pagamenti, questo la esclude prima ancora di cominciare.
Questo percorso non ha alcuna scadenza del processo applicativo. Continua a funzionare finché l'account di hosting resta attivo ed entro i suoi limiti, e smette di essere adatto non appena l'app ha bisogno di lavoro lato server al momento della richiesta.
Dove finiscono i percorsi gratuiti
I percorsi gratuiti falliscono in punti diversi, non tutti insieme. Un host statico risolve già l'uptime del laptop e ti dà un URL stabile del provider. Un host per app gratuito può fare lo stesso, e alcuni ora includono storage persistente. Un VPS inizia ad avere senso quando i requisiti si accumulano: il tuo processo lato server deve restare online, ti serve storage persistente prevedibile, e le regole su risorse o utilizzo del piano gratuito non bastano più.
Quella soglia va rispettata. Una demo non è un motivo per comprare un server, e nemmeno un progetto statico a basso traffico. Resta su un tunnel per condivisioni di breve durata, resta su un host statico finché l'app è davvero statica, e resta su un host per app gratuito finché i suoi limiti sono adatti a ciò che esegui.
Passa a un VPS quando ti serve un server sempre acceso che controlli tu e sei disposto ad assumerti il lavoro operativo che ne deriva. Il nostro VPS Linux è un'opzione, e un VPS comparabile di un altro provider può fare lo stesso lavoro. Dimensiona il server per l'app invece di dare per scontato che il piano più piccolo basti.
Costruisci su un VPS Linux con accesso root, NVMe e la potenza di AMD EPYC.
Vedi piani LinuxDue cose cambiano quando lo fai. Il flusso di deploy con push su git che ti dava una piattaforma di hosting non è più automatico. Puoi ricrearlo con un PaaS self-hosted come Coolify o Dokku, o costruire il tuo percorso CI/CD, e in entrambi i casi aggiornamenti e manutenzione ora sono affar tuo.
L'altro cambiamento è che la macchina contiene più della sola app. Sarà felice di far girare Code Server e Claude Code se preferisci che anche il tuo editor viva lì, il che è o un bel bonus o un intero nuovo weekend di lavoro, a seconda di te.
Domande frequenti
Perché il mio URL pubblico ha smesso di funzionare dopo aver chiuso il laptop?
Perché il tunnel era legato al processo che l'ha creato, non alla tua app. Chiudere il laptop o terminare il terminale interrompe quel processo, l'URL pubblico muore, e la tua app sta benissimo. Riavviare il tunnel ti dà un nuovo URL, a meno che lo strumento non ti assegni un dominio riservato. Se il link deve sopravvivere alla sospensione del laptop, l'app deve uscire dalla tua macchina.
Mi serve un nome di dominio per mettere un'app locale su un URL pubblico?
No, in quasi tutti i casi. Un quick tunnel di Cloudflare, il dominio di sviluppo assegnato da ngrok, localtunnel, localhost.run e i piani di hosting gratuiti qui sopra ti assegnano tutti gratuitamente un sottodominio sul loro dominio. L'eccezione è un tunnel Cloudflare con nome, che richiede un dominio già aggiunto a Cloudflare DNS.
Qualcuno su un'altra rete Wi-Fi può aprire il mio indirizzo IP locale?
No. Un indirizzo come 192.168.1.42 punta al dispositivo che lo detiene sulla rete a cui sei collegato in questo momento, che su qualsiasi altra rete è un dispositivo diverso o niente del tutto. Chiunque sia fuori dal tuo Wi-Fi ha bisogno invece di un tunnel, di un piano di hosting gratuito o di un host statico.
Quali di questi funzionano se la mia app ha un login e un database?
I percorsi stessa rete e tunnel funzionano senza modifiche perché l'app gira ancora sulla tua macchina. Anche un deploy statico può funzionare se le richieste di login e database vanno direttamente dal browser a un servizio gestito e nessun codice privato lato server deve girare a ogni richiesta. Se il tuo codice di autenticazione o database ha bisogno di un server al momento della richiesta, usa invece un host lato server. Su un host per app gratuito, tieni i dati persistenti in uno storage durevole invece di dare per scontato che il filesystem locale dell'app sopravviva.

Discussione
Commenti
Accedi per partecipare alla discussione.