SSD Nodes Learn
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-07-24

Installare LAMP su Ubuntu 24.04 con PHP-FPM

Guida completa per configurare Apache, MariaDB con unix_socket, PHP 8.3 tramite PHP-FPM e HTTPS con Certbot su Ubuntu 24.04 senza errori di configurazione.

Cosa stai costruendo

Uno stack LAMP è composto da quattro componenti su un server Ubuntu 24.04: Linux come sistema operativo, Apache per il protocollo HTTP, MariaDB per i dati e PHP 8.3 per l'esecuzione del codice. Al termine della procedura avrai un virtual host basato su nome che serve una directory applicativa reale, un database con un utente dedicato con privilegi minimi, PHP integrato in Apache tramite PHP-FPM e un certificato Let's Encrypt gratuito.

L'installazione consiste in quattro comandi apt. Quasi tutto in questa guida riguarda il collegamento tra i componenti e i piccoli errori che causano problemi comuni: uno stack appena installato che mostra una pagina bianca, il download del codice sorgente tramite il browser o l'impossibilità di accedere al database appena installato. Ognuno di questi errori presenta una firma specifica; ogni errore è elencato di seguito con il testo esatto che visualizzerai.

Prerequisiti e potenziali problemi

Si assume l'utilizzo di un VPS Ubuntu 24.04 KVM appena installato con un utente sudo o root e un indirizzo IPv4 pubblico. Lo stack minimale richiede 1 GB di RAM; si consigliano 2 GB prima di installare un'applicazione con database, poiché i buffer predefiniti di MariaDB e i processi PHP-FPM consumano rapidamente il primo gigabyte.

Per far funzionare Certbot alla fine della procedura, devono essere soddisfatti due requisiti. È necessario un nome di dominio con un record A che punti all'IP pubblico del VPS: Let's Encrypt effettua la validazione via HTTP su tale nome; un semplice indirizzo IP non può ottenere un certificato. Inoltre, le porte 80 e 443 devono essere raggiungibili da internet; su molti provider, ciò richiede l'apertura delle porte nel firewall di rete del pannello di controllo oltre a ufw sul server. Le modifiche DNS possono richiedere fino a un'ora per la propagazione; configurare il record A in anticipo per garantirne la disponibilità.

Step 1 - Install Apache and confirm the default page

sudo apt update
sudo apt install -y apache2

apt avvia e abilita il servizio automaticamente. Verifica lo stato con:

systemctl status apache2

Devi visualizzare una riga con active (running). Ora apri http://YOUR_SERVER_IP/ in un browser. La Apache2 Ubuntu Default Page con il banner "It works!" è il risultato corretto — conferma che Apache è attivo e non è un errore. La pagina si trova all'indirizzo /var/www/html/index.html ed è gestita dal virtual host predefinito 000-default.conf. Disabiliterai entrambi in seguito; per ora la loro presenza è corretta.

Se la pagina non si carica ma systemctl indica che il processo è in esecuzione, un firewall sta bloccando la connessione. Il passaggio successivo riguarda questo problema.

Step 2 - Aprire il firewall per HTTP e HTTPS

Il pacchetto apache2 registra tre profili applicativi ufw. Elencali:

sudo ufw app list

Vedrai Apache, Apache Full e Apache Secure. Apache riguarda solo la porta 80, Apache Secure riguarda solo la porta 443 e Apache Full riguarda entrambe — questa è l'opzione corretta, poiché aggiungerai TLS in seguito.

sudo ufw allow OpenSSH
sudo ufw allow "Apache Full"
sudo ufw enable

Abilita OpenSSH prima di eseguire ufw enable. ufw nega tutto il traffico in entrata per impostazione predefinita; abilitarlo senza una regola SSH interrompe la connessione corrente non appena la regola diventa attiva — la sessione attuale rimane attiva, ma non potrai più riconnetterti. Conferma con sudo ufw status; i valori OpenSSH, Apache Full e i relativi equivalenti v6 devono essere tutti impostati su ALLOW.

Step 3 - Install MariaDB and secure it

sudo apt install -y mariadb-server
systemctl status mariadb

Ubuntu 24.04 include MariaDB 10.11, una versione long-term-support, quindi non è necessario un repository esterno. Con il servizio attivo, procedere alla messa in sicurezza:

sudo mysql_secure_installation

È consigliabile leggere i prompt invece di premere ripetutamente Enter. Quando viene richiesto l'attuale password root, premere Enter: non è ancora impostata alcuna password. Quando viene chiesto "Switch to unix_socket authentication?", la risposta non cambia nulla poiché è già abilitata in questo pacchetto, quindi premere n. Rispondere n a "Change the root password?" per il motivo indicato nel paragrafo successivo, quindi rispondere Y a tutto il resto: rimuovere gli utenti anonimi, disabilitare il login root remoto, eliminare il database di test e ricaricare le tabelle dei privilegi.

Questa è la parte che genera confusione. Su MariaDB per Ubuntu, l'account database root utilizza l'autenticazione unix_socket invece di una password. Ciò significa che il database si fida dell'utente dell'operating-system con cui si è già effettuato l'accesso. Pertanto, il comando funziona da una shell root:

sudo mysql

...e si accede a un prompt MariaDB [(none)]> senza che venga chiesta alcuna password. Lo stesso comando eseguito come utente non privilegiato viene rifiutato, che è l'obiettivo principale: l'accesso al root del database è legato a sudo sul sistema, e non esiste una password da rubare, sottoporre a phishing o brute-force. Questo metodo è più sicuro di una password, quindi non modificarlo. La regola che ne deriva è: non puntare mai un'applicazione verso l'account root. Creare un utente dedicato per ogni applicazione (Step 7), poiché un'app che si connette via TCP con username e password non può comunque utilizzare l'autenticazione socket, e ogni app deve essere limitata al proprio database.

Step 4 - Install PHP 8.3 with PHP-FPM

La versione predefinita di PHP su Ubuntu 24.04 è la 8.3. Installa il process manager FPM e le estensioni necessarie per un'applicazione tipica:

sudo apt install -y php8.3-fpm php8.3-mysql php8.3-cli \
  php8.3-curl php8.3-xml php8.3-mbstring php8.3-zip

Nota cosa non è presente nell'elenco: libapache2-mod-php. Quel pacchetto datato incorpora un interprete PHP all'interno di ogni processo Apache. È una soluzione semplice, ma ogni worker mantiene una copia di PHP sia che stia servendo uno script o un'immagine statica; i due condividono lo stesso ciclo di vita e funziona solo con l'MPM prefork di Apache, il meno efficiente. PHP-FPM, invece, esegue PHP come un pool di processi indipendente con cui Apache comunica tramite un socket. Apache può quindi utilizzare l'MPM event (threaded) per i file statici e inoltrare solo le richieste PHP; il pool può essere configurato indipendentemente dal web server e la stessa configurazione FPM è utilizzabile anche se si installa nginx come proxy inverso. È lo standard attuale per motivi tecnici.

Apache comunica con FPM tramite il modulo proxy_fcgi. Abilita il modulo, abilita la configurazione inclusa nel pacchetto FPM e riavvia il servizio:

sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.3-fpm
sudo systemctl restart apache2

a2enconf php8.3-fpm attiva /etc/apache2/conf-available/php8.3-fpm.conf, che contiene la regola per instradare i file PHP verso il socket FPM. Il componente principale intercetta ogni file .php e lo inoltra al socket situato in /run/php/php8.3-fpm.sock:

<FilesMatch ".+\.ph(ar|p|tml)$">
    SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>

Non è necessario modificare quel file; la configurazione predefinita è corretta. Tuttavia, conoscere il percorso del socket è fondamentale per diagnosticare in seguito gli errori "PHP downloads instead of running" e "Primary script unknown": entrambi derivano da un disallineamento tra Apache e FPM riguardo al socket o al file associato.

Step 5 - Un virtual host basato sul nome per la tua app

L'hosting virtuale basato sul nome permette a un singolo IP di servire più siti; Apache seleziona il sito tramite l'header Host: nella richiesta. Crea una directory per l'app, separata dalla directory predefinita /var/www/html:

sudo mkdir -p /var/www/testapp
sudo chown -R www-data:www-data /var/www/testapp
sudo chmod -R 755 /var/www/testapp

La gestione dei permessi è fondamentale. Apache e PHP-FPM su Ubuntu vengono eseguiti come l'utente www-data, quindi i file che il web server deve leggere — e le directory in cui l'app deve scrivere, come una cartella di upload — devono essere di proprietà di www-data. Se devi anche modificare i file con il tuo utente di login, una pratica comune è possedere i file personalmente e aggiungere il proprio utente al gruppo www-data; per un deploy standard, www-data:www-data è l'opzione più semplice.

Crea il virtual host in /etc/apache2/sites-available/testapp.conf:

<VirtualHost *:80>
    ServerName app.example.com
    DocumentRoot /var/www/testapp

    <Directory /var/www/testapp>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/testapp-error.log
    CustomLog ${APACHE_LOG_DIR}/testapp-access.log combined
</VirtualHost>

Imposta ServerName con il tuo dominio reale. Options -Indexes impedisce ad Apache di elencare il contenuto della directory in assenza di un file index; senza questa opzione, i visitatori possono navigare l'intera struttura delle cartelle del codice sorgente. AllowOverride All permette il funzionamento di un file .htaccess, necessario per le "pretty URLs" nella maggior parte delle applicazioni PHP; rimuovilo da None per un leggero incremento delle prestazioni se la tua app non lo richiede. Abilita questo sito, disabilita quello predefinito, verifica la configurazione e ricarica:

sudo a2ensite testapp
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2

apache2ctl configtest deve stampare Syntax OK. La riga a2dissite 000-default è quella che spesso viene dimenticata, causando il problema della pagina predefinita che appare bloccata — dettagliato nella sezione relativa agli errori.

Step 6 - Verifica l'esecuzione di PHP e rimuovi il file di prova

Crea un file PHP di una riga nella directory root dell'app:

echo "<?php phpinfo();" | sudo tee /var/www/testapp/info.php

Accedi a http://app.example.com/info.php. Il risultato corretto è la tabella PHP Version 8.3.x di colore viola e grigio che elenca i moduli caricati, con la riga Server API che riporta FPM/FastCGI. Quest'ultima riga conferma che le richieste passano tramite PHP-FPM e non mod_php.

Ora elimina immediatamente il file:

sudo rm /var/www/testapp/info.php

phpinfo() espone la versione esatta di PHP, ogni estensione caricata, i percorsi dei file e i dettagli dell'ambiente. Queste informazioni sono utili a chiunque esegua scansioni sul server per trovare versioni con vulnerabilità note. Si tratta di un test, non di una funzionalità. Elimina il file non appena hai visualizzato la pagina. Se il browser propone di scaricare info.php invece di mostrare la tabella, PHP non è configurato correttamente con Apache; vai alla sezione relativa agli errori prima di procedere.

Step 7 - Creare il database dell'app e un utente con privilegi minimi

Aprire il database come root tramite socket-authentication:

sudo mysql

Successivamente, creare un database e un utente con accesso limitato esclusivamente a quel database:

CREATE DATABASE appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Sono state effettuate tre scelte deliberate. utf8mb4 è il vero UTF-8 a quattro byte; il vecchio alias utf8 tronca silenziosamente le emoji e alcuni caratteri CJK, quindi utilizzare sempre utf8mb4. Il grant è su appdb.*, non su *.*: questo utente può accedere solo al proprio database e nient'altro; in questo modo, una vulnerabilità di SQL-injection nell'app non può leggere le tabelle di altri siti. 'appuser'@'localhost' restringe l'account alle connessioni provenienti dalla macchina stessa.

Testare l'accesso come utente specificato:

mysql -u appuser -p appdb

Il sistema richiede la password e apre un prompt MariaDB [appdb]>. Si noti l'assenza del flag -h: lasciandolo disattivato, il client si connette tramite il socket Unix locale, che è esattamente ciò che MariaDB considera localhost. Un dettaglio importante: per MySQL e MariaDB, localhost indica il socket Unix e 127.0.0.1 indica una connessione TCP. Su una versione standard di MariaDB su Ubuntu 24.04, il server risolve ancora una connessione TCP da 127.0.0.1 a localhost, quindi entrambi corrispondono all'account; tuttavia, su server con skip-name-resolve abilitato (un tweak comune per le prestazioni e lo standard in molte immagini container), i due sono considerati host diversi; un'app che tenta di connettersi tramite 127.0.0.1 riceverà un errore ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' (using password: YES) anche se la password è corretta.

Configurare quindi l'applicazione su host localhost, utente appuser e database appdb — mai su root. Sia mysqli di PHP che PDO utilizzano il socket Unix quando l'host è la stringa letterale localhost, corrispondendo all'account appena creato. Se un framework richiede un host TCP numerico, creare l'utente in base al metodo di connessione effettivo: 'appuser'@'127.0.0.1', oppure @'%' (insieme a una regola del firewall) solo se è necessario raggiungere il database da un'altra macchina.

Step 8 - Aggiungere HTTPS con Certbot

Servire un modulo di login tramite HTTP non criptato invia le password in chiaro. Ogni browser moderno segnalerà la pagina come "Non sicura". Certbot risolve il problema con un singolo comando. Installalo utilizzando il plugin per Apache:

sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache

Certbot utilizza due plugin in questo contesto. L'apache authenticator conferma il controllo del dominio servendo temporaneamente un file di challenge tramite Apache; l'apache installer riscrive il virtual host per aggiungere il blocco 443, punta al nuovo certificato e reindirizza tutto il traffico HTTP verso HTTPS di default. Dalla versione 2.0 di Certbot non viene posta la domanda sul reindirizzamento; passa --no-redirect se è necessario continuare a servire traffico HTTP semplice. Poiché è stato impostato un ServerName reale nello Step 5, Certbot rileva automaticamente il dominio. I certificati hanno una validità di 90 giorni e il pacchetto installa un timer systemd per il rinnovo automatico; verifica il timer con sudo certbot renew --dry-run, che deve terminare con Congratulations, all simulated renewals succeeded.

Per la guida completa sulla challenge, sul timer di rinnovo e sui requisiti DNS e firewall, consulta la guida di approfondimento su emettere certificati TLS Let's Encrypt gratuiti con Certbot su Apache.

Backups, upgrades, and hardening

Esegui il backup dei due elementi che contengono lo stato del sistema: i database e la web root. Un dump logico notturno è l'approccio più semplice e affidabile — sudo sh -c 'mysqldump --all-databases --single-transaction | gzip > /root/db-$(date +%F).sql.gz', da copiare successivamente fuori dal server. È fondamentale avvolgere l'intera pipeline in sudo sh -c: senza questo passaggio, la shell esegue il redirect > /root/... con il proprio utente e fallisce con Permission denied, poiché solo mysqldump ha ereditato il sudo. --single-transaction fornisce uno snapshot coerente delle tabelle InnoDB senza bloccarle. Abbinandolo a un tar di /var/www e /etc/apache2/sites-available, è possibile ricostruire l'intero stack su un nuovo VPS partendo da tali file.

Gli upgrade sono operazioni comuni di tipo sudo apt update && sudo apt upgrade. Il problema critico è il passaggio di versione PHP — quando una futura versione di Ubuntu imposterà come predefinita la versione PHP 8.4, apt potrebbe installare php8.4-fpm insieme alla 8.3, il socket diventerà /run/php/php8.4-fpm.sock e la configurazione di Apache punterà ancora al socket della 8.3. Abilita la nuova conf (sudo a2enconf php8.4-fpm) e disabilita la vecchia, altrimenti il sito restituirà Primary script unknown dopo un upgrade altrimenti di routine. Poiché i rilasci di PHP sono più frequenti rispetto a una distro LTS, consulta le note di rilascio di PHP invece di bloccare una specifica patch version.

Due passaggi di hardening sono consigliati il primo giorno. Primo, installa Fail2Ban per monitorare SSH sul server — un VPS pubblico riceve tentativi di login automatizzati entro pochi minuti; una jail limitata trasforma migliaia di tentativi in pochi casi prima del ban. Secondo, se preferisci gestire gli Apache virtual hosts, i database MariaDB e gli utenti tramite browser invece di modificare manualmente i file, il pannello di controllo web Webmin opera su questo esatto stack e gestisce gli stessi file di configurazione appena creati. Nessuno dei due sostituisce la conoscenza dei singoli componenti, ma entrambi riducono l'attrito operativo quotidiano.

Modalità di errore, con le stringhe che vedrai

La pagina predefinita non scompare. Hai modificato il virtual host, hai ricaricato, ma il browser mostra ancora "Apache2 Ubuntu Default Page" con il banner "It works!". Apache serve il primo virtual host corrispondente; se nessun ServerName corrisponde alla richiesta, vince il primo file di configurazione in ordine alfabetico — 000-default.conf viene prima di testapp.conf. Il nome host della richiesta non corrisponde al tuo ServerName, oppure non hai eseguito sudo a2dissite 000-default. Disabilita il default, sudo systemctl reload apache2, e conferma con apache2ctl -S, che stampa la mappa dei vhost e mostra quale configurazione gestisce il default. Svuota anche la cache del browser; un codice 200 memorizzato dalla vecchia pagina può persistere.

Un file .php viene scaricato invece di essere eseguito. Apri info.php e il browser scarica un file contenente il codice sorgente <?php grezzo, oppure lo mostra come testo semplice, invece di eseguirlo. Apache sta servendo il file come asset statico perché l'handler PHP non è collegato — hai saltato sudo a2enmod proxy_fcgi, o sudo a2enconf php8.3-fpm, o non hai riavviato Apache successivamente. Esegui tutti e tre i passaggi (Step 4) e ricarica. Conferma che il modulo sia caricato con apache2ctl -M | grep fcgi, che dovrebbe elencare proxy_fcgi_module. Questo è un leak di codice sorgente, non un bug estetico; risolvi il problema prima di caricare dati reali sul server.

ERROR 1698 (28000): Access denied for user 'root'@'localhost'. Hai eseguito mysql -u root o mariadb -u root senza sudo. L'account root utilizza l'autenticazione unix_socket, quindi accetta la connessione solo se l'utente del sistema operativo è effettivamente root. La soluzione è sudo mysql — niente -u root, niente password. Questo messaggio è il comportamento previsto di un'autenticazione socket funzionante, non un'installazione corrotta.

ERROR 1045 (28000): Access denied for user 'appuser'@'127.0.0.1' dall'applicazione, con la password corretta. L'account esiste come 'appuser'@'localhost', ma l'app si sta connettendo via TCP a 127.0.0.1 su un server dove la risoluzione del nome host è disabilitata (skip-name-resolve); di conseguenza MariaDB considera i due come host diversi — localhost è il socket Unix, 127.0.0.1 è TCP. Punta l'app all'host localhost per usare il socket e far corrispondere l'account, oppure crea un secondo account 'appuser'@'127.0.0.1' se il framework supporta solo TCP.

AH01071: Got error 'Primary script unknown' in /var/log/apache2/testapp-error.log, con il browser che mostra File not found.. Apache ha passato la richiesta a PHP-FPM, ma FPM non ha trovato lo script nel percorso fornito da Apache. Due cause comuni: il socket FPM nella tua configurazione punta a una versione di PHP non installata (un socket php8.4 dopo un upgrade mentre è attivo solo PHP 8.3), oppure il file non esiste perché DocumentRoot e la directory reale non coincidono. Verifica che il socket esista con ls -l /run/php/, conferma che DocumentRoot corrisponda alla posizione del file, e riavvia sia php8.3-fpm che apache2.

AH00558: apache2: Could not reliably determine the server's fully qualified domain name ad ogni riavvio. Questo è un avviso innocuo, non un errore — Apache ti sta comunicando che non è impostato alcun ServerName globale. Silenzialo scrivendo ServerName your.domain in /etc/apache2/conf-available/servername.conf ed eseguendo sudo a2enconf servername.

(98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80 all'avvio di Apache. Un altro web server occupa già la porta 80 — spesso un'istanza residua di nginx da un esperimento precedente. Trovalo con sudo ss -ltnp | grep :80, quindi ferma e disabilita l'altro servizio prima di avviare Apache.

FAQ

mod_php o PHP-FPM - quale dovrei usare?

Usa PHP-FPM. mod_php integra un interprete in ogni processo Apache e forza l'uso del lento MPM prefork; di conseguenza Apache mantiene l'overhead di PHP anche quando serve un'immagine statica. PHP-FPM esegue PHP come un pool separato e configurabile indipendentemente che Apache interroga tramite un socket; questo permette di usare il più veloce MPM event e facilita la migrazione a nginx in futuro. È lo standard moderno; mod_php è utile solo per applicazioni legacy che richiedono comportamenti in-process.

Perché il browser scarica il file PHP invece di eseguirlo?

Apache tratta il file .php come un download statico perché non è associato alcun handler PHP. Su Ubuntu 24.04 con FPM, ciò significa che manca uno dei passaggi sudo a2enmod proxy_fcgi, sudo a2enconf php8.3-fpm o il riavvio di Apache. Esegui tutti e tre i passaggi e ricarica, quindi verifica con apache2ctl -M | grep fcgi che proxy_fcgi_module sia presente nell'elenco. Finché il problema non viene risolto, il server espone il codice sorgente; tratta la questione come urgente.

Perché l'accesso root su MariaDB è negato anche con la password corretta?

Perché non esiste una password: la versione MariaDB di Ubuntu autentica l'account root tramite unix_socket, collegandolo all'utente root del sistema operativo. L'esecuzione di mysql -u root da una shell standard restituisce ERROR 1698 (28000): Access denied for user 'root'@'localhost' per impostazione predefinita. Connettiti invece tramite sudo mysql e crea un utente con autenticazione tramite password per ogni applicazione, invece di riutilizzare root.

Come posso aggiungere HTTPS al mio sito LAMP?

Installa certbot e python3-certbot-apache, punta l'A record del dominio verso il server e infine esegui sudo certbot --apache. L'autenticatore di Apache verifica il controllo del dominio tramite il processo Apache in esecuzione; l'installer riscrive il virtual host per la porta 443 e configura il rinnovo automatico. Il guida completa a Certbot e Apache spiega la procedura, il timer di rinnovo e i comuni errori.

#lamp#apache#mariadb#php-fpm#ubuntu