Vai al contenuto
Posta Pannello Scegli un piano

Errore 404 WordPress su tutte le pagine tranne la home: soluzioni

Errore 404 WordPress su tutte le pagine tranne la home: soluzioni

Se la home di WordPress si carica ma articoli, pagine e categorie restituiscono 404, o la richiesta non arriva mai a index.php (in Nginx manca il try_files corretto, in Apache manca il .htaccess, mod_rewrite è disattivato oppure AllowOverride è None), oppure WordPress non ha alcuna regola di riscrittura per quell’URL. Per prima cosa scoprite chi ha generato la pagina 404: se non ha la grafica del tema, va corretto il server web; se è la pagina 404 del vostro tema, eseguite wp rewrite flush (oppure salvate Impostazioni → Permalink) e cercate un plugin che rompe le regole. Abbiamo riprodotto sei cause su un banco di prova; qui sotto trovate i codici di risposta reali e le correzioni che abbiamo verificato.

Come abbiamo testato

  • Ambiente: banco di prova RichHost, container isolato con 2 vCPU / 8 GB, Ubuntu 24.04, nginx 1.24, PHP 8.3-FPM, MariaDB 10.11, WordPress 6.7.2, tema Twenty Twenty-Five, permalink /%postname%/. Per i test con Apache abbiamo installato temporaneamente Apache 2.4.58 su una porta separata. Data: ottobre 2026.
  • Abbiamo rotto una copia separata del sito con un proprio database (nginx sulla porta 8090, Apache sulla 8091), così il banco principale è rimasto intatto. La copia, il suo database e Apache sono stati poi rimossi.
  • Ogni stato è stato verificato con curl: codice di risposta, numero di occorrenze di wp-content nell’HTML (segno che la pagina è stata generata da WordPress) e intestazione X-Cache. L’output è abbreviato.
  • Sono risultati del banco di prova, non misurazioni dei server di produzione di RichHost.

Primo passo: chi ha restituito il 404, il server o WordPress

Prendete un qualsiasi URL di articolo che restituisce 404 ed eseguite tre comandi:

curl -s -o /dev/null -w "%{http_code}\n" https://example.com/post-name/
curl -s https://example.com/post-name/ | grep -c wp-content
curl -sI "https://example.com/?p=123" | grep -iE "^HTTP|^location"

Sul banco, la pagina 404 di nginx o di Apache conteneva 0 occorrenze di wp-content, mentre la pagina 404 di Twenty Twenty-Five ne conteneva 4. Zero significa che la richiesta non è mai arrivata a PHP e bisogna correggere la configurazione del server web. Un numero maggiore di zero significa che WordPress ha ricevuto la richiesta ma non ha trovato una regola corrispondente.

Il controllo con l’indirizzo semplice /?p=ID è utile, ma non si comporta come dicono molte guide: non restituisce 200, reindirizza (301) all’URL leggibile. È successo in tutti gli scenari che abbiamo provato: /?p=304 restituiva 301 verso /post-300/, che poi restituiva 404. Il reindirizzamento stesso dimostra che PHP e il database funzionano e che a essere rotta è la riscrittura degli URL.

Tabella dei codici di risposta della home WordPress e di un articolo con sei guasti riprodotti sul banco di prova
Codici di risposta ottenuti con curl sul banco di prova: la home funziona quasi sempre, quindi non dimostra nulla

Causa 1. Nginx senza il try_files corretto

Nginx ignora il .htaccess, quindi tutto dipende dal blocco location /. Abbiamo fatto girare la copia del sito con tre versioni di questo blocco:

URL try_files $uri $uri/ /index.php?$args; try_files $uri $uri/ =404; senza location /
/ 200 200 200
/post-300/ 200 404 404
/sample-page/ 200 404 404
/category/uncategorized/ 200 404 404
/wp-json/ 200 404 404
/?p=304 301 301 301

La home funziona sempre perché per “/” nginx trova index.php tramite la direttiva index. La riga try_files $uri $uri/ =404; arriva dal sito predefinito di Ubuntu, ed è per questo che finisce spesso nelle configurazioni di WordPress. Con questa riga error.log restava vuoto, quindi il log non vi porterà alla causa. Senza alcun blocco location /, invece, il log dà un indizio:

[error] ... "/var/www/r10wp/sample-page/index.php" is not found (2: No such file or directory), request: "GET /sample-page/ HTTP/1.1"

Anche /wp-json/ smetteva di funzionare, quindi l’editor a blocchi non riesce a salvare gli articoli. Intanto /?rest_route=/ restituiva 200: la REST API funziona, fallisce solo la riscrittura. Il blocco con cui sul banco ogni URL ha restituito 200:

server {
    listen 80;
    server_name example.com;
    root /var/www/example.com/public_html;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
}
sudo nginx -T | grep -n try_files
sudo nginx -t && sudo systemctl reload nginx

Sostituite la versione di PHP nel percorso del socket con la vostra. Altri blocchi Nginx, compresi i reindirizzamenti verso https e verso l’host senza www, sono raccolti nei nostri esempi pronti di redirect 301.

Causa 2. Apache: .htaccess mancante, mod_rewrite disattivato o AllowOverride None

Per questa parte abbiamo installato Apache 2.4.58 con PHP-FPM su una porta separata per la stessa copia del sito. Quattro guasti diversi hanno prodotto quasi lo stesso risultato:

Stato Home Articolo Log degli errori di Apache
Nessun .htaccess 200 404 vuoto
.htaccess con il blocco IfModule, mod_rewrite disattivato 200 404 vuoto
.htaccess senza IfModule, mod_rewrite disattivato 500 500 Invalid command ‘RewriteEngine’
mod_rewrite attivo, ma AllowOverride None 200 404 vuoto
mod_rewrite attivo, AllowOverride All 200 200 nessun errore

Quindi una semplice pagina “Not Found” di Apache con il log vuoto è normale per tre cause su quattro. Tre controlli permettono di distinguerle:

ls -la .htaccess                # is the file in the site root
apache2ctl -M | grep rewrite    # expect rewrite_module (shared)
# AllowOverride test: add any random word as the first line of .htaccess

Abbiamo verificato anche il test della parola a caso. Con AllowOverride None il sito ha continuato a restituire 404, perché il file viene ignorato. Con AllowOverride All ha restituito subito 500, e nel log è comparso “Invalid command” con quella parola. Dopo il test togliete la riga.

Il blocco standard per un singolo sito, generato da WordPress stesso sul banco:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

Su un VPS dove gestite Apache da soli, attivate il modulo e consentite il .htaccess per la directory del sito (dopo questo, sul banco ogni URL ha restituito 200):

sudo a2enmod rewrite
sudo systemctl restart apache2

# in the virtual host config
<Directory /var/www/example.com/public_html>
    AllowOverride All
    Require all granted
</Directory>

Una trappola di WP-CLI: wp rewrite flush --hard non ha creato il .htaccess e ha mostrato invece un avviso:

$ wp rewrite flush --hard --path=/var/www/r10wp
Warning: Regenerating a .htaccess file requires special configuration. See usage docs.
Success: Rewrite rules flushed.

Un file wp-cli.yml con apache_modules risolve il problema, ma WP-CLI cerca questo file nella directory corrente, non in quella indicata con --path. Ha funzionato solo così:

cd /var/www/example.com/public_html
printf 'apache_modules:\n  - mod_rewrite\n' > wp-cli.yml
wp rewrite flush --hard

Il file è stato creato con proprietario root, perché il comando girava come root. Sull’hosting eseguite WP-CLI come utente del sito oppure cambiate il proprietario; i permessi del .htaccess devono essere 644. Altre impostazioni del server web sono trattate nella nostra guida alla configurazione del server.

Causa 3. Struttura dei permalink cambiata, regole non rigenerate

WordPress salva le regole di riscrittura nel database (l’opzione rewrite_rules) e le rigenera solo con un “flush”: quando si salva la pagina Permalink, quando lo fanno alcuni plugin o quando eseguite wp rewrite flush. Se la struttura viene cambiata direttamente nel database o con wp option update (capita dopo importazioni del database e script di migrazione), i link del sito sono già nuovi mentre le regole sono ancora vecchie:

$ wp option update permalink_structure '/%year%/%monthnum%/%postname%/'
Success: Updated 'permalink_structure' option.

/2026/10/post-300/   404  (theme 404 page, wp-content: 4)
/post-300/           301  → /2026/10/post-300/
/sample-page/        200

$ wp rewrite list --match=/2026/10/post-300/ --fields=match,query
(.?.+?)(?:/([0-9]+))?/?$   index.php?pagename=$matches[1]&page=$matches[2]

I vecchi URL reindirizzano ai nuovi e i nuovi restituiscono 404, quindi il visitatore trova un errore su ogni link del menu. wp rewrite list --match mostra quale regola è scattata per prima: qui l’URL è stato preso dalla regola delle pagine, perché nel database non ci sono regole per la nuova struttura. La correzione:

$ wp rewrite flush
Success: Rewrite rules flushed.
$ wp rewrite list --format=count
96

/2026/10/post-300/   200

I nuovi tipi di contenuto si comportano allo stesso modo. Abbiamo registrato un tipo di contenuto book in un mu-plugin: /book/test-book/ restituiva la pagina 404 del tema, anche se /?book=test-book si apriva con 200. Dopo wp rewrite flush il numero di regole è passato da 94 a 111 e l’articolo si è aperto. Per questo un 404 solo sui prodotti WooCommerce o su un altro tipo di contenuto dipende quasi sempre da regole non rigenerate. La stessa causa è frequente dopo aver trasferito WordPress su un altro hosting importando il database a mano.

Causa 4. home e siteurl sbagliati, o installazione in una sottodirectory

Il sito è passato da /blog/ alla radice, il database ha mantenuto il vecchio URL

Abbiamo impostato home e siteurl sull’URL con /blog mentre i file stavano nella radice e Nginx era configurato correttamente. Gli articoli si aprivano comunque con 200 sia su /post-300/ sia su /blog/post-300/: WordPress interpreta entrambi. Ma tutti i link nell’HTML puntavano a /blog/, gli stili da /blog/wp-content/ restituivano 404 e /wp-admin/ reindirizzava a /blog/wp-login.php, che restituiva anch’esso 404. Quindi qui il sintomo è diverso: un sito senza stili e senza accesso all’amministrazione.

wp option get home
wp option get siteurl
wp option update home https://example.com
wp option update siteurl https://example.com

Dopo i due comandi di aggiornamento, sul banco gli stili hanno restituito 200 e /wp-admin/ ha portato alla pagina di accesso corretta. Se non riuscite a entrare nell’amministrazione e non avete SSH, impostate gli stessi valori con le costanti WP_HOME e WP_SITEURL in wp-config.php. I vecchi URL nel contenuto degli articoli si sostituiscono con wp search-replace (eseguitelo prima con --dry-run); in questo test non l’abbiamo eseguito.

WordPress in una sottodirectory, Nginx ripiega sull’index.php della radice

I file di WordPress erano in /blog/ e la configurazione aveva solo location / con ripiego su /index.php. Risultato: /blog/ restituiva 200, /blog/post-300/ e /blog/sample-page/ restituivano la pagina 404 di nginx, stili e wp-login.php restituivano 200. È lo stesso “funziona solo la home”, ma per una sottodirectory: nginx cerca /index.php nella radice, dove non c’è. La correzione dopo la quale ogni URL ha restituito 200:

location /blog/ {
    try_files $uri $uri/ /blog/index.php?$args;
}

Su Apache, un’installazione in sottodirectory cambia due righe del .htaccess: RewriteBase /blog/ e RewriteRule . /blog/index.php [L]. Questa variante non l’abbiamo provata sul banco.

Causa 5. Un plugin o un tema con una regola di riscrittura in conflitto

I plugin che “rimuovono la base dagli URL” (perché landing page o prodotti si aprano come /nome/) aggiungono una propria regola in cima all’elenco. Lo abbiamo riprodotto con un mu-plugin che registra un tipo di contenuto personalizzato e una regola:

add_rewrite_rule('^([^/]+)/?$', 'index.php?landing=$matches[1]', 'top');
  • Subito dopo l’installazione non è cambiato nulla: le regole di un plugin arrivano nel database solo dopo un flush. Per questo il guasto spesso compare più tardi, quando qualcuno salva i permalink o aggiorna un altro plugin.
  • Dopo wp rewrite flush la home e /category/uncategorized/ restituivano 200, mentre /post-300/ e /sample-page/ restituivano la pagina 404 del tema.
  • Dopo aver rimosso il plugin senza flush è andata peggio: /post-300/ restituiva 200 ma con il contenuto della home (titolo del sito, body class “home blog”). Un motore di ricerca vedrebbe decine di duplicati della home al posto degli articoli.
  • Un altro wp rewrite flush ha rimesso tutto a posto: 200 e l’articolo giusto.

Lo stesso comando trova il colpevole:

$ wp rewrite list --match=/post-300/ --fields=match,query,source
^([^/]+)/?$                 index.php?landing=$matches[1]                       other
(.?.+?)(?:/([0-9]+))?/?$    index.php?pagename=$matches[1]&page=$matches[2]   page

La sorgente “other” indica una regola aggiunta dal codice di un plugin o di un tema, non dal core. Poi disattivate i plugin sospetti uno alla volta (meglio su una copia del sito) ed eseguite wp rewrite flush dopo ogni passo. Un dettaglio curioso: la stessa regola troppo ampia che punta a pagine normali (pagename) non ha rotto nulla. WordPress la salta quando nessuna pagina ha quel percorso e passa alla regola successiva. A rompere il sito sono le regole che puntano a query var personalizzate.

Causa 6. La cache continua a servire un 404 salvato dopo la correzione

Abbiamo attivato fastcgi_cache con fastcgi_cache_valid 404 10m;. Prima osservazione: WordPress invia il suo 404 con Cache-Control: no-cache e una data Expires del 1984, quindi per impostazione predefinita nginx non lo mette in cache (X-Cache: MISS a ogni richiesta). Il problema è comparso quando abbiamo aggiunto fastcgi_ignore_headers Cache-Control Expires Set-Cookie;, una riga spesso consigliata per la cache di WordPress:

broken               /post-300/        404  X-Cache: MISS
                     /post-300/        404  X-Cache: HIT
rules fixed          /post-300/        404  X-Cache: HIT
                     /post-300/?utm=1  200  X-Cache: MISS
cache entry deleted  /post-300/        200  X-Cache: MISS
                     /post-300/        200  X-Cache: HIT

Il segnale rivelatore: l’URL con un parametro qualsiasi si apre, mentre l’URL senza parametri restituisce 404 con X-Cache: HIT. Potete cancellare una singola voce senza il modulo purge, perché il nome del file è l’md5 della chiave di cache. Sul banco quel file conteneva “KEY: httpGET127.0.0.1/post-300/” e “Status: 404 Not Found”; una volta cancellato, l’articolo ha restituito 200.

# key = $scheme$request_method$host$request_uri, levels=1:2
KEY="httpsGETexample.com/post-name/"
H=$(echo -n "$KEY" | md5sum | cut -d' ' -f1)
sudo rm -f /var/cache/nginx/wp/${H: -1}/${H: -3:2}/$H

# or the whole cache
sudo find /var/cache/nginx/wp -type f -delete

Prendete il percorso della cache dalla vostra direttiva fastcgi_cache_path. Svuotate separatamente la cache del plugin e la CDN. Perché gli errori non restino appiccicati, mettete in cache i 404 per poco tempo, per esempio fastcgi_cache_valid 404 1m;. Come la cache lato server influisce sulla velocità lo spieghiamo nell’articolo su cache e RAM nell’hosting.

Tabella decisionale: sintomo, causa, controllo, correzione

Sintomo Causa Controllo Correzione
404 senza la grafica del tema (pagina nginx), log vuoto try_files ... =404 in location / nginx -T | grep try_files try_files $uri $uri/ /index.php?$args;
Pagina 404 di nginx, il log dice “index.php is not found” Nessun blocco location / Log degli errori di nginx Aggiungere location / con try_files
Pagina “Not Found” di Apache, log vuoto Nessun .htaccess, mod_rewrite disattivato o AllowOverride None ls -la .htaccess, apache2ctl -M, test della parola a caso Ripristinare il .htaccess, a2enmod rewrite, AllowOverride All
500 su tutte le pagine, home compresa .htaccess senza IfModule con mod_rewrite disattivato “Invalid command ‘RewriteEngine'” nel log a2enmod rewrite
404 del tema su tutti gli articoli, i vecchi URL restituiscono 301 Struttura dei permalink cambiata senza flush wp rewrite list --match=/url/ wp rewrite flush
404 del tema su un solo tipo di contenuto Nuovo tipo di contenuto, regole non rigenerate /?type=slug si apre wp rewrite flush
404 del tema sugli URL a un solo segmento, le categorie funzionano Plugin con una regola troppo ampia La prima regola in wp rewrite list --match ha sorgente other Disattivare il plugin, wp rewrite flush
Qualsiasi URL a un solo segmento mostra la home con 200 Plugin rimosso, la sua regola è rimasta nel database Body class “home” sull’URL di un articolo wp rewrite flush
Il 404 resta dopo la correzione, l’URL con parametro restituisce 200 La cache ha salvato il 404 curl -sI url | grep -i x-cache → HIT Cancellare la voce o tutta la cache
Sito senza stili, l’accesso all’amministrazione restituisce 404 home e siteurl sbagliati wp option get home wp option update home e siteurl
/blog/ funziona, /blog/post/ restituisce il 404 di nginx Installazione in sottodirectory, ripiego su /index.php della radice Configurazione di location location /blog/ con /blog/index.php?$args

Domande frequenti

Perché dopo il passaggio a un nuovo hosting funziona solo la home?

Il più delle volte è andato perso il file nascosto .htaccess (i client FTP spesso nascondono e saltano i file che iniziano con un punto) oppure il nuovo server usa Nginx senza il try_files necessario. Sul banco entrambi i casi davano lo stesso quadro: 200 sulla home e 404 sugli articoli.

Come capisco se il mio sito è servito da Nginx o da Apache?

Eseguite curl -sI https://example.com/ | grep -i server. Sul banco l’intestazione era “Server: nginx/1.24.0 (Ubuntu)”. Alcuni provider la nascondono; in quel caso controllate il tipo di server web nel pannello di controllo. Se nginx fa da proxy davanti ad Apache, le regole di WordPress le esegue Apache tramite il .htaccess.

Reinstallare WordPress serve?

No. In nessuno dei sei scenari la causa erano i file del core: le regole stanno nel database, e passare le richieste a index.php è compito del server web.

Qualche giorno di 404 danneggia il posizionamento su Google?

Un breve disservizio di solito passa senza conseguenze, ma se il crawler vede 404 per diversi giorni, le pagine iniziano a uscire dall’indice. Dopo la correzione, controllate alcuni URL con lo strumento Controllo URL di Search Console e inviate di nuovo la sitemap.

Se preferite non configurare da soli Nginx, Apache e la cache, date un’occhiata ai piani di hosting RichHost: dischi NVMe, pannello di controllo proprietario con assistente IA e SSL Let’s Encrypt gratuito. In una guida a parte spieghiamo anche come scegliere l’hosting per WordPress.

Altro su questo argomento

Tutti gli articoli
Errore 403 Forbidden: cause e come risolverlo
Risoluzione dei problemi di hosting12.11.2026

Errore 403 Forbidden: cause e come risolverlo

Cosa significa l’errore 403 Forbidden e come trovarne la causa: permessi dei file, .htaccess, nginx, blocco IP, protezione hotlink, WAF e plugin…

Hosting con dominio gratis: .com, .org o .info
Tipi di hosting12.11.2026

Hosting con dominio gratis: .com, .org o .info

Dominio gratis .com, .org o .info se paghi l'hosting per un anno. Piani da 8 $/mese con NVMe, siti ed email illimitati.…

Sottodominio: cos’è, come crearlo, record DNS e SSL
Tipi di hosting11.11.2026

Sottodominio: cos’è, come crearlo, record DNS e SSL

Un sottodominio si crea nel DNS senza costi. Come aggiungerlo sull'hosting, quale record serve, come ottenere l'SSL e quando conviene una sottocartella.