51 06/04/2026 10 min

Apache come proxy inverso su Ubuntu

Apache come proxy inverso è una scelta solida quando vuoi esporre un’app interna senza pubblicarla direttamente su Internet. Il web server riceve le richieste dall’esterno, applica regole di routing, TLS, header di sicurezza, compressione e logging, poi inoltra il traffico al servizio backend su porta locale, socket Unix o host remoto.

Su Ubuntu la configurazione è semplice, ma conviene impostarla bene fin dall’inizio: moduli corretti, VirtualHost puliti, gestione degli header, time-out coerenti con l’app e verifiche rapide per capire dove si rompe il flusso. Qui si lavora con Apache 2.4 su Ubuntu recente, ma la logica resta la stessa anche su versioni vicine.

Quando ha senso usare Apache come reverse proxy

Il proxy inverso è utile se vuoi:

  • pubblicare più applicazioni dietro un solo IP e un solo certificato TLS;
  • separare il livello web dal livello applicativo;
  • aggiungere autenticazione, rate limit, header o redirect centralizzati;
  • terminare TLS su Apache e parlare in HTTP verso servizi interni;
  • mettere davanti applicazioni che ascoltano solo su localhost o su una rete privata.

È una soluzione tipica per app Node.js, Python, Go, Java, PHP-FPM dietro backend applicativi, dashboard interne, pannelli e servizi containerizzati. Se hai già un bilanciatore esterno o un CDN, Apache può stare dietro a quello oppure essere il punto di ingresso finale.

Architettura minima

Lo schema base è questo:

Client -> Apache su Ubuntu -> backend app su 127.0.0.1:porta oppure host interno

Apache può inoltrare richieste HTTP, WebSocket e in alcuni casi anche bilanciare tra più backend. Per la maggior parte delle installazioni bastano i moduli proxy standard e un VirtualHost dedicato per ogni sito.

Prerequisiti sul server Ubuntu

Prima di toccare Apache, verifica che il backend risponda localmente. Se il servizio non va già da solo, il proxy non lo risolve.

Controlli utili:

sudo systemctl status apache2
curl -I http://127.0.0.1:3000

Se il backend è su un altro host, prova prima la raggiungibilità di rete e la porta:

nc -vz 10.0.0.20 3000

Se usi HTTPS verso il backend, verifica anche il certificato e il nome host, soprattutto se il backend presenta un certificato valido solo per un dominio preciso.

Abilitare i moduli necessari

Su Ubuntu Apache usa il sistema dei moduli abilitabili. Per un reverse proxy classico servono almeno proxy e proxy_http. Se devi gestire WebSocket, spesso serve anche proxy_wstunnel. Per header e rewrite possono servire altri moduli, ma non vanno attivati a caso.

sudo a2enmod proxy proxy_http headers rewrite ssl

Se l’app usa WebSocket:

sudo a2enmod proxy_wstunnel

Ricarica Apache dopo l’abilitazione dei moduli:

sudo apache2ctl configtest && sudo systemctl reload apache2

Il test di sintassi deve restituire Syntax OK. Se fallisce, correggi prima di ricaricare.

VirtualHost base per reverse proxy

Il modo più pulito è creare un VirtualHost dedicato, ad esempio in /etc/apache2/sites-available/app.example.com.conf. Un esempio minimo con backend locale su porta 3000:

<VirtualHost *:80>
    ServerName app.example.com

    ProxyPreserveHost On
    ProxyRequests Off

    ProxyPass / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/

    ErrorLog ${APACHE_LOG_DIR}/app_error.log
    CustomLog ${APACHE_LOG_DIR}/app_access.log combined
</VirtualHost>

La coppia ProxyPass e ProxyPassReverse è il cuore della configurazione. La prima inoltra il traffico, la seconda riscrive gli header di redirect del backend in modo che il client resti coerente sul dominio pubblico.

ProxyPreserveHost On conserva l’host originale richiesto dal client. È spesso la scelta giusta per app che fanno routing in base all’host o che generano URL assoluti. Se il backend si aspetta invece un host interno, puoi lasciarlo su Off, ma va deciso consapevolmente.

Abilitare il sito e fare il primo test

Dopo aver creato il file del VirtualHost:

sudo a2ensite app.example.com.conf
sudo apache2ctl configtest
sudo systemctl reload apache2

Verifica dall’esterno con:

curl -I http://app.example.com

Atteso: risposta 200, 301 o 302 coerente con la tua app. Se vedi 502, 503 o 504, il problema è quasi sempre nel backend, nel routing o nei time-out. Se vedi la pagina di default di Apache, probabilmente il VirtualHost non è stato agganciato come previsto.

Reverse proxy con HTTPS

In produzione quasi sempre conviene terminare TLS su Apache. In questo modo il traffico tra client e front-end è cifrato, mentre il tratto verso il backend può restare interno e più semplice da gestire.

Un VirtualHost HTTPS tipico:

<VirtualHost *:443>
    ServerName app.example.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/app.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/app.example.com/privkey.pem

    ProxyPreserveHost On
    ProxyRequests Off

    ProxyPass / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/

    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"

    ErrorLog ${APACHE_LOG_DIR}/app_ssl_error.log
    CustomLog ${APACHE_LOG_DIR}/app_ssl_access.log combined
</VirtualHost>

Molte applicazioni devono sapere che il client arriva via HTTPS, altrimenti generano redirect errati o cookie non sicuri. Per questo gli header X-Forwarded-Proto e X-Forwarded-Port sono importanti.

Se usi Let’s Encrypt con certbot, la gestione del certificato può essere automatizzata. In quel caso verifica che il rinnovo non rompa il VirtualHost e che Apache ricarichi correttamente la nuova chiave.

Gestione degli header forwardati

Dietro un proxy, il backend non vede più direttamente il client. Per questo conviene passare gli header standard in modo esplicito:

RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
RequestHeader set X-Forwarded-Host "%{Host}e" env=Host

Apache spesso aggiunge già alcuni header di suo, ma non dare per scontato che bastino per tutte le applicazioni. Alcuni framework si aspettano anche X-Forwarded-For, che rappresenta l’IP del client originale. Apache lo gestisce normalmente, ma va verificato lato applicazione se il trust del proxy è configurato correttamente.

Se l’app genera redirect assoluti verso l’host interno, controlla:

  • ProxyPreserveHost;
  • config dell’app per “base URL” o “trusted proxies”;
  • eventuali rewrite nel backend;
  • header X-Forwarded-Proto e X-Forwarded-Host.

WebSocket e connessioni persistenti

Per applicazioni in tempo reale, dashboard live o chat, il reverse proxy deve supportare WebSocket. In Apache questo richiede in genere proxy_wstunnel e una regola dedicata.

<VirtualHost *:443>
    ServerName ws.example.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/ws.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/ws.example.com/privkey.pem

    ProxyPreserveHost On
    ProxyRequests Off

    ProxyPass "/socket"  "ws://127.0.0.1:3000/socket"
    ProxyPassReverse "/socket"  "ws://127.0.0.1:3000/socket"

    ProxyPass "/" "http://127.0.0.1:3000/"
    ProxyPassReverse "/" "http://127.0.0.1:3000/"
</VirtualHost>

Ordine e specificità contano. Le regole più specifiche devono stare prima di quelle generiche, altrimenti il traffico WebSocket finisce nella regola HTTP standard e la connessione si rompe.

Timeout, limiti e comportamento sotto carico

Un proxy inverso non deve tagliare richieste lente senza motivo, ma nemmeno tenere appese connessioni troppo a lungo. I timeout vanno allineati al comportamento reale del backend.

Parametri spesso utili:

  • ProxyTimeout per il tempo massimo di attesa verso il backend;
  • Timeout globale di Apache;
  • eventuali limiti del backend su request body e keep-alive;
  • dimensione massima di upload se l’app gestisce file grandi.

Esempio:

<VirtualHost *:443>
    ServerName app.example.com
    ProxyTimeout 60
</VirtualHost>

Se vedi 504 Gateway Timeout, non aumentare i timeout a caso. Prima misura la latenza reale del backend e capisci se il problema è codice, database, disco o saturazione CPU.

Proxy verso backend su host remoto

Il backend non deve per forza stare sulla stessa macchina. Puoi puntare a un host interno, per esempio un nodo applicativo in rete privata:

ProxyPass / http://10.0.0.20:8080/
ProxyPassReverse / http://10.0.0.20:8080/

In questo scenario conviene:

  • limitare l’esposizione del backend solo alla rete interna;
  • bloccare l’accesso diretto dall’esterno con firewall o security group;
  • verificare la risoluzione DNS se usi un nome host anziché un IP;
  • allineare i certificati se il backend usa HTTPS.

Se il backend remoto è instabile, il proxy può diventare un punto di concentrazione del problema. Per questo sono utili log separati e metriche sulla latenza upstream.

Logging e diagnostica

Per capire rapidamente dove si rompe il flusso, tieni separati i log del VirtualHost. È molto più comodo che scavare nel log globale di Apache.

Controlli utili:

sudo tail -f /var/log/apache2/app_error.log
sudo tail -f /var/log/apache2/app_access.log

Se il backend risponde ma il client vede errore, cerca messaggi come:

  • AH01114: HTTP: failed to make connection to backend
  • AH00898: Error reading from remote server
  • proxy: error reading status line from remote server

Questi indizi distinguono tra problema di connettività, backend morto, risposta malformata o timeout. Se non hai log dettagliati, abilita temporaneamente un livello più verboso solo sul VirtualHost interessato, poi riportalo alla normalità.

Hardening minimo del proxy

Un reverse proxy esposto su Internet va trattato come superficie critica. Alcune misure minime hanno un buon rapporto costo/beneficio:

  • ProxyRequests Off per evitare di trasformare Apache in un open proxy;
  • solo i moduli necessari, niente extra inutili;
  • backend vincolato a localhost o rete privata;
  • TLS moderno e certificati validi;
  • header di sicurezza coerenti con l’app;
  • permessi stretti sui file di configurazione e sulle chiavi private.

Se il proxy termina TLS, assicurati che le chiavi private siano leggibili solo da root o dall’utente previsto dal sistema, e che non finiscano in backup non protetti.

Bilanciamento semplice tra più backend

Apache può anche distribuire richieste su più backend. Per casi semplici, puoi definire un bilanciamento base con mod_proxy_balancer. È utile quando hai più istanze della stessa app e vuoi una disponibilità un po’ migliore o una distribuzione del carico elementare.

Un esempio concettuale:

<Proxy "balancer://appcluster">
    BalancerMember http://10.0.0.21:8080
    BalancerMember http://10.0.0.22:8080
</Proxy>

ProxyPass / balancer://appcluster/
ProxyPassReverse / balancer://appcluster/

Qui conviene verificare bene la persistenza di sessione se l’app non è stateless. Se l’autenticazione dipende da cookie di sessione locali, il bilanciamento senza sticky session può creare logout casuali o errori intermittenti.

Troubleshooting rapido

Quando qualcosa non va, la sequenza utile è sempre la stessa: verifica DNS, poi Apache, poi backend, poi rete interna, poi applicazione. Non partire dal codice se non hai ancora confermato che il backend risponda.

Test minimi:

curl -I https://app.example.com
curl -I http://127.0.0.1:3000
sudo apache2ctl configtest

Se la risposta pubblica è 502 ma il backend locale risponde, guarda la configurazione del VirtualHost, il path di ProxyPass, la reachability verso l’host remoto e i log di Apache. Se il backend non risponde neppure localmente, il problema non è Apache.

Se vedi contenuti misti, redirect errati o loop infiniti, controlla i redirect nel backend e la coerenza tra HTTP e HTTPS. Molti errori nascono da una mancata comprensione del fatto che il backend “vede” una richiesta diversa da quella ricevuta dal client.

Esempio completo pronto da adattare

Questo esempio mostra un VirtualHost HTTPS con proxy verso un backend locale, header base e log separati:

<VirtualHost *:443>
    ServerName app.example.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/app.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/app.example.com/privkey.pem

    ProxyRequests Off
    ProxyPreserveHost On
    ProxyTimeout 60

    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"

    ProxyPass / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/

    ErrorLog ${APACHE_LOG_DIR}/app_error.log
    CustomLog ${APACHE_LOG_DIR}/app_access.log combined
</VirtualHost>

Dopo averlo inserito, fai sempre questa sequenza:

  1. sudo apache2ctl configtest
  2. sudo systemctl reload apache2
  3. curl -I https://app.example.com
  4. controllo log su /var/log/apache2/app_error.log

Se serve un percorso specifico per API o WebSocket, aggiungilo con regole più precise prima della regola generica. Se devi fare manutenzione, puoi anche usare temporaneamente una pagina di fallback o un backend alternativo, ma va gestito con attenzione per non introdurre incoerenze.

Checklist operativa finale

  • moduli corretti abilitati;
  • VirtualHost dedicato e non ambiguo;
  • ProxyRequests Off;
  • ProxyPass e ProxyPassReverse coerenti;
  • header forwarded allineati all’app;
  • backend raggiungibile prima di mettere in produzione;
  • log separati per diagnosi rapida;
  • test con curl prima e dopo il reload;
  • certificati e rinnovi verificati se usi HTTPS.

Con questa base, Apache su Ubuntu funziona bene come reverse proxy per la maggior parte degli scenari classici: siti dinamici, API, pannelli interni e servizi applicativi dietro un front-end unico.