Installare WordPress su un VPS invece che su un hosting condiviso dà controllo completo su prestazioni, sicurezza e costi, a patto di configurare bene le parti che un hosting gestisce per voi. In questa guida l’installazione avviene interamente da terminale con WP-CLI, sullo stack Nginx, PHP 8.5 e MariaDB di Ubuntu 26.04, e arriva fino alla produzione: HTTPS, permessi corretti, cron di sistema, backup e cache. La versione corrente di WordPress è la 7.1.4.
In breve
- Tempo: 45–60 minuti.
- Difficoltà: media.
- Cosa serve: un VPS Ubuntu 26.04 LTS (o Debian 13) con Nginx, PHP-FPM e MariaDB già installati, un dominio con record DNS che punta al server, la porta 80 e 443 aperte.
Versioni di riferimento: WordPress 7.1.4, WP-CLI (Phar ufficiale), PHP 8.5.4, nginx 1.28.3, MariaDB 11.8.6 su Ubuntu 26.04 LTS; su Debian 13 sostituite 8.5 con 8.4 in pacchetti, percorsi e socket. Procedura verificata sulla documentazione ufficiale al 4 novembre 2026.
Prerequisiti e backup
Questa guida parte dallo stack descritto in https://hardwarfare.it/ai-software/linux/installare-nginx-php-fpm-mariadb-ubuntu-lemp/: Nginx attivo, PHP 8.5 con le estensioni mysql, curl, xml, mbstring, intl, zip e gd, MariaDB messo in sicurezza. Il requisito consigliato da WordPress è PHP 8.3 o superiore con MariaDB 10.11 o MySQL 8.0 o superiori: Ubuntu 26.04 li supera tutti. Se state migrando un sito esistente, esportate prima database e cartella wp-content dal vecchio hosting.
I segnaposto da sostituire: TUO_DOMINIO (il dominio), WPUTENTE (l’utente di sistema del sito), NOME_DB e UTENTE_DB (database e utente MariaDB).
Passo 1: database, utente di sistema e cartelle
Creiamo il database con il suo utente:
sudo mariadb
CREATE DATABASE NOME_DB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'UTENTE_DB'@'localhost' IDENTIFIED BY 'PASSWORD_ROBUSTA';
GRANT ALL PRIVILEGES ON NOME_DB.* TO 'UTENTE_DB'@'localhost';
EXIT;Poi un utente di sistema dedicato al sito, senza shell di login. Sarà il proprietario dei file e l’utente con cui gira PHP per questo sito: così WordPress può aggiornarsi da solo, ma un sito compromesso non può toccare gli altri presenti sul server.
sudo useradd --system --home-dir /var/www/TUO_DOMINIO --shell /usr/sbin/nologin WPUTENTE
sudo mkdir -p /var/www/TUO_DOMINIO/public /var/www/TUO_DOMINIO/backup
sudo chown -R WPUTENTE:WPUTENTE /var/www/TUO_DOMINIO
sudo chmod 755 /var/www/TUO_DOMINIO /var/www/TUO_DOMINIO/publicLa cartella backup sta fuori da public, quindi non è raggiungibile dal web.
Passo 2: un pool PHP-FPM dedicato
Il pool predefinito www gira come www-data. Ne creiamo uno che gira come WPUTENTE, con un proprio socket:
sudo nano /etc/php/8.5/fpm/pool.d/TUO_DOMINIO.conf
[TUO_DOMINIO]
user = WPUTENTE
group = WPUTENTE
listen = /run/php/php8.5-fpm-TUO_DOMINIO.sock
; Nginx (www-data) deve poter scrivere sul socket
listen.owner = www-data
listen.group = www-data
pm = ondemand
pm.max_children = 8
pm.process_idle_timeout = 10s
pm.max_requests = 500
; dimensione massima dei caricamenti
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64M
sudo php-fpm8.5 -t
sudo systemctl reload php8.5-fpmVerifica: ls -l /run/php/ deve mostrare php8.5-fpm-TUO_DOMINIO.sock di proprietà di www-data. Il valore di pm.max_children va adattato alla RAM: il metodo di calcolo è nella guida allo stack LEMP.
Passo 3: il server block di Nginx per WordPress
sudo nano /etc/nginx/sites-available/TUO_DOMINIO
server {
listen 80;
listen [::]:80;
server_name TUO_DOMINIO www.TUO_DOMINIO;
root /var/www/TUO_DOMINIO/public;
index index.php;
# deve corrispondere a upload_max_filesize del pool
client_max_body_size 64M;
access_log /var/log/nginx/TUO_DOMINIO.access.log;
error_log /var/log/nginx/TUO_DOMINIO.error.log;
location / {
# permalink "belli": se il file non esiste, decide WordPress
try_files $uri $uri/ /index.php?$args;
}
location ~ .php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.5-fpm-TUO_DOMINIO.sock;
}
# file nascosti, tranne la cartella usata da Let's Encrypt
location ~ /.(?!well-known) {
deny all;
}
# nessun PHP eseguibile nella cartella dei caricamenti
location ~* /wp-content/uploads/.*.php$ {
deny all;
}
}La regola try_files $uri $uri/ /index.php?$args; è quella indicata dalla documentazione di WordPress per Nginx. Il blocco sugli uploads impedisce che un file PHP caricato tramite un plugin vulnerabile venga eseguito: è uno degli interventi di hardening più utili e meno citati.
sudo ln -s /etc/nginx/sites-available/TUO_DOMINIO /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxPasso 4: HTTPS prima di installare
Conviene ottenere il certificato prima dell’installazione, così WordPress nasce già con indirizzi https:// e non serve correggerli dopo:
sudo certbot --nginx -d TUO_DOMINIO -d www.TUO_DOMINIOInstallazione di Certbot, rinnovo e risoluzione dei problemi sono spiegati in https://hardwarfare.it/ai-software/linux/certificato-https-lets-encrypt-certbot/.
Verifica: https://TUO_DOMINIO deve rispondere con il lucchetto (per ora con un errore 403 o 404, perché la cartella è vuota), e http://TUO_DOMINIO deve reindirizzare a HTTPS.
Passo 5: installare WP-CLI
Il metodo raccomandato è il file Phar ufficiale:
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wpIl manuale di WP-CLI suggerisce anche di verificare la firma GPG del file prima di usarlo (file wp-cli.phar.asc nello stesso repository). Per gli aggiornamenti futuri basta sudo wp cli update.
Verifica: wp --info deve mostrare la versione di WP-CLI e PHP 8.5.
Passo 6: scaricare, configurare e installare WordPress
Tutti i comandi WP-CLI si eseguono come WPUTENTE, così i file nascono con il proprietario giusto. L’opzione -H di sudo imposta la cartella home dell’utente, dove WP-CLI tiene la propria cache.
cd /var/www/TUO_DOMINIO/public
sudo -u WPUTENTE -H wp core download --locale=it_IT
sudo -u WPUTENTE -H wp config create --dbname=NOME_DB --dbuser=UTENTE_DB --prompt=dbpass --locale=it_ITCon --prompt=dbpass la password del database viene chiesta a video e non finisce nella cronologia della shell. wp config create verifica anche che la connessione al database funzioni e genera automaticamente chiavi e salt. Ora l’installazione vera e propria, con lo stesso accorgimento per la password dell’amministratore:
sudo -u WPUTENTE -H wp core install --url=https://TUO_DOMINIO --title="Nome del sito" --admin_user=NOMEADMIN --admin_email=TUA_EMAIL --locale=it_IT --prompt=admin_passwordEvitate admin come nome dell’amministratore: è il primo che i bot provano.
Verifica: sudo -u WPUTENTE -H wp core version deve rispondere 7.1.4 (o successiva); sudo -u WPUTENTE -H wp option get siteurl deve restituire l’indirizzo in https://. Aprite https://TUO_DOMINIO/wp-admin/ ed entrate con le credenziali appena scelte.
Passo 7: hardening concreto di wp-config e permessi
Due costanti da impostare subito con WP-CLI:
sudo -u WPUTENTE -H wp config set DISALLOW_FILE_EDIT true --raw
sudo -u WPUTENTE -H wp config set DISABLE_WP_CRON true --rawDISALLOW_FILE_EDIT toglie dalla bacheca l’editor di temi e plugin: la documentazione di hardening di WordPress lo indica come il primo strumento che usa un attaccante entrato con un account amministratore, perché permette di eseguire codice. DISABLE_WP_CRON spegne il cron interno, che sostituiamo al passo 8. L’opzione --raw scrive true come valore booleano e non come stringa.
Permessi: WordPress raccomanda 755 per le cartelle e 644 per i file. Il file wp-config.php, che contiene la password del database, può essere ancora più chiuso: in questa configurazione lo legge solo PHP, che gira come WPUTENTE, mentre Nginx non ne ha bisogno.
sudo find /var/www/TUO_DOMINIO/public -type d -exec chmod 755 {} ;
sudo find /var/www/TUO_DOMINIO/public -type f -exec chmod 644 {} ;
sudo chmod 600 /var/www/TUO_DOMINIO/public/wp-config.phpVerifica: sudo -u WPUTENTE -H wp config get DISALLOW_FILE_EDIT deve restituire 1; nella bacheca, sotto Aspetto e Plugin, le voci dell’editor dei file di temi e plugin non devono più comparire. Il sito deve continuare a caricarsi normalmente dopo il cambio di permessi.
Passo 8: un cron vero al posto di WP-Cron
WP-Cron parte solo quando qualcuno visita il sito: su un sito poco visitato le operazioni programmate (pubblicazioni, backup dei plugin, aggiornamenti) arrivano in ritardo, su uno molto visitato si aggiunge lavoro a ogni richiesta. Con un cron di sistema le esecuzioni diventano regolari. Aprite il crontab dell’utente del sito:
sudo crontab -u WPUTENTE -e
*/5 * * * * /usr/local/bin/wp --path=/var/www/TUO_DOMINIO/public cron event run --due-now > /dev/null 2>&1Il percorso completo di wp è necessario perché cron usa un PATH ridotto che spesso non include /usr/local/bin.
Verifica: dopo qualche minuto, sudo -u WPUTENTE -H wp cron event list mostra gli eventi programmati: le date della colonna di prossima esecuzione devono avanzare, senza eventi rimasti indietro nel passato.
Passo 9: backup del database e cache
Un’esportazione del database si fa con un comando:
sudo -u WPUTENTE -H wp db export /var/www/TUO_DOMINIO/backup/db-$(date +%F).sqlUn backup sullo stesso server, però, protegge dagli errori ma non dalla perdita del VPS: copiate fuori i dump e la cartella wp-content, per esempio con restic e la regola 3-2-1 descritta in https://hardwarfare.it/ai-software/linux/backup-automatici-server-restic-regola-3-2-1/. Lo stesso comando può andare nel crontab di WPUTENTE, una volta al giorno (in cron il simbolo % va scritto %).
Per la cache degli oggetti, utile soprattutto con molti plugin o WooCommerce, Redis è la soluzione più diffusa:
sudo apt install redis-server php8.5-redis
sudo systemctl reload php8.5-fpm
sudo -u WPUTENTE -H wp plugin install redis-cache --activate
sudo -u WPUTENTE -H wp redis enableVerifica: sudo -u WPUTENTE -H wp redis status deve indicare lo stato “Connected”. Per la cache di pagina, l’alternativa lato server è la FastCGI cache di Nginx, più veloce di qualsiasi plugin ma da configurare con attenzione per non servire pagine personalizzate (carrello, utenti collegati) ad altri visitatori.
Gli aggiornamenti si gestiscono da terminale con wp core update, wp plugin update --all e wp theme update --all, sempre come WPUTENTE e dopo un backup del database.
Se qualcosa va storto
Error establishing a database connection: credenziali sbagliate inwp-config.phpo MariaDB fermo. Provate l’accesso a mano conmariadb -u UTENTE_DB -p NOME_DB; se funziona, controllateDB_NAME,DB_USEReDB_HOSTconsudo -u WPUTENTE -H wp config get DB_NAME.- 502 Bad Gateway: Nginx non trova il socket del pool. Il percorso in
fastcgi_passdeve coincidere conlistendel pool; controllate anchesudo journalctl -u php8.5-fpmper errori nel file del pool. Error: This does not seem to be a WordPress installation.da WP-CLI: siete nella cartella sbagliata. Spostatevi in/var/www/TUO_DOMINIO/publico aggiungete--path=/var/www/TUO_DOMINIO/public.- Tutte le pagine tranne la home danno 404: manca
/index.php?$argsnellatry_filesdel bloccolocation /. 413 Request Entity Too Largecaricando un file:client_max_body_sizedi Nginx è più basso del file. Allineatelo conupload_max_filesizeepost_max_sizedel pool.- WordPress chiede credenziali FTP per installare plugin: i file non appartengono all’utente con cui gira PHP. Ripetete
sudo chown -R WPUTENTE:WPUTENTE /var/www/TUO_DOMINIO. - Reindirizzamenti infiniti dopo aver attivato HTTPS:
siteurlehomesono rimasti inhttp://. Correggete conwp option update siteurl https://TUO_DOMINIOewp option update home https://TUO_DOMINIO.
Come tornare indietro
Per eliminare il sito: disattivate il server block (sudo rm /etc/nginx/sites-enabled/TUO_DOMINIO, sudo nginx -t, sudo systemctl reload nginx), togliete il pool (sudo rm /etc/php/8.5/fpm/pool.d/TUO_DOMINIO.conf e sudo systemctl reload php8.5-fpm) e il crontab (sudo crontab -u WPUTENTE -r).
Attenzione: i passi seguenti cancellano definitivamente contenuti e database. Esportate prima il database e copiate la cartella del sito fuori dal server. Poi, in sudo mariadb: DROP DATABASE NOME_DB; e DROP USER 'UTENTE_DB'@'localhost';; infine sudo rm -r /var/www/TUO_DOMINIO e sudo userdel WPUTENTE.
La nostra lettura
La parte che fa davvero la differenza rispetto a un’installazione “da cinque minuti” sono tre dettagli: utente e pool PHP dedicati, nessun PHP eseguibile negli uploads, cron di sistema. Con questi accorgimenti un VPS da pochi euro al mese regge bene un sito WordPress di medie dimensioni. Per completare la sicurezza del server guardate https://hardwarfare.it/guide-tecniche/guide-server-self-hosting/proteggere-server-linux-ssh-fail2ban-ufw/.
Fonti
- WordPress, come installare
- WP-CLI Handbook, installazione
- WP-CLI, wp core install
- WP-CLI, wp config create
- WordPress, hardening
- WordPress, Nginx
- WordPress Hosting Handbook
- WordPress, requisiti
Immagine di copertina: Laptop coding programs (Unsplash).jpg di Tirza van Dijk tirzavandijk, licenza CC0, via Wikimedia Commons; elaborazione grafica HardWarfare.
Articolo redatto con il supporto di strumenti di intelligenza artificiale e verificato dalla redazione. Come usiamo l'AI · Come lavoriamo
