Installare WordPress su un VPS con Nginx, WP-CLI e HTTPS: guida da zero alla produzione

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/public

La 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-fpm

Verifica: 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 nginx

Passo 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_DOMINIO

Installazione 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/wp

Il 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_IT

Con --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_password

Evitate 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 --raw

DISALLOW_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.php

Verifica: 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>&1

Il 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).sql

Un 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 enable

Verifica: 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 in wp-config.php o MariaDB fermo. Provate l’accesso a mano con mariadb -u UTENTE_DB -p NOME_DB; se funziona, controllate DB_NAME, DB_USER e DB_HOST con sudo -u WPUTENTE -H wp config get DB_NAME.
  • 502 Bad Gateway: Nginx non trova il socket del pool. Il percorso in fastcgi_pass deve coincidere con listen del pool; controllate anche sudo journalctl -u php8.5-fpm per 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/public o aggiungete --path=/var/www/TUO_DOMINIO/public.
  • Tutte le pagine tranne la home danno 404: manca /index.php?$args nella try_files del blocco location /.
  • 413 Request Entity Too Large caricando un file: client_max_body_size di Nginx è più basso del file. Allineatelo con upload_max_filesize e post_max_size del 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: siteurl e home sono rimasti in http://. Correggete con wp option update siteurl https://TUO_DOMINIO e wp 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/.

Articolo redatto con il supporto di strumenti di intelligenza artificiale e verificato dalla redazione. Come usiamo l'AI · Come lavoriamo

Redazione HardWarfare
Redazione HardWarfarehttps://hardwarfare.it/chi-siamo/
La redazione di HardWarfare segue hardware PC, gaming, smartphone, sicurezza informatica e mercato tech. Ogni articolo parte da fonti primarie linkate in calce, viene verificato da un revisore diverso da chi lo scrive e si aggiorna quando i fatti cambiano. Scriveteci da Contatti per segnalazioni e correzioni.

Da non perdere

Articoli correlati

LASCIA UN COMMENTO

Scrivi il tuo commento!
Inserisci il tuo nome