Di cosa parlerò in questa pagina
Il problema: due URL diversi mostravano la stessa pagina WordPress
Mi sono trovata davanti a un comportamento piuttosto particolare su un sito WordPress multilingua che utilizza WPML insieme a Permalink Manager Lite.
Una pagina in lingua inglese aveva correttamente questo permalink:
/en/how-to-start-an-online-discount-store/
Il problema era che risultava perfettamente raggiungibile anche utilizzando lo slug della corrispondente pagina francese:
/en/comment-lancer-une-boutique-de-reductions-en-ligne/
Entrambi gli URL restituivano:
HTTP 200 OK
e mostravano esattamente la stessa pagina in inglese.
Dal punto di vista SEO non è una situazione ideale: due URL diversi in grado di restituire lo stesso contenuto possono creare duplicazioni, sprecare crawl budget e rendere più difficile comprendere quale URL debba essere considerato quello principale.
La cosa ancora più curiosa era che il canonical risultava corretto.
Aprendo:
/en/comment-lancer-une-boutique-de-reductions-en-ligne/
nel codice HTML era presente:
<link rel="canonical" href="https://www.example.com/en/how-to-start-an-online-discount-store/">
Quindi WordPress e il sistema SEO sapevano perfettamente quale fosse l’URL corretto.
Il problema era un altro: perché l’URL con lo slug francese continuava a essere considerato valido e restituiva HTTP 200 anziché un redirect o un 404?
La configurazione iniziale
Il sito utilizzava:
- WordPress;
- WPML per la gestione delle lingue;
- Permalink Manager Lite per la gestione personalizzata dei permalink;
- URL delle lingue organizzati tramite directory, ad esempio
/fr/e/en/.
La pagina francese aveva uno slug simile a:
comment-lancer-une-boutique-de-reductions-en-ligne
mentre la sua traduzione inglese utilizzava correttamente:
comment-lancer-une-boutique-de-reductions-en-ligne
La situazione che si presentava era quindi questa:
/fr/comment-lancer-une-boutique-de-reductions-en-ligne/ → pagina francese
/en/how-to-start-an-online-discount-store/ → pagina inglese
/en/comment-lancer-une-boutique-de-reductions-en-ligne/ → pagina inglese(!)
L’ultimo URL, teoricamente, non sarebbe dovuto esistere.
Primo tentativo: controllare lo slug WordPress e Permalink Manager
La prima ipotesi era piuttosto logica: da qualche parte doveva essere stato memorizzato il vecchio permalink.
Ho quindi controllato sia lo slug nativo di WordPress sia quello gestito da Permalink Manager.
Nella pagina inglese lo slug risultava correttamente:
how-to-start-an-online-discount-store
Anche il permalink mostrato da Permalink Manager era corretto.
Non c’era quindi nessuna traccia evidente dello slug:
comment-lancer-une-boutique-de-reductions-en-ligne
associato alla pagina inglese.
Questo escludeva il caso più semplice: un permalink personalizzato errato salvato direttamente sulla pagina.
Secondo tentativo: controllare _wp_old_slug
WordPress conserva alcuni vecchi slug nel database utilizzando il meta field:
_wp_old_slug
Questo permette, ad esempio, di redirigere automaticamente un vecchio indirizzo quando viene modificato lo slug di un articolo.
Ho quindi verificato se lo slug francese fosse stato memorizzato come vecchio slug della pagina inglese.
Una possibile query è:
SELECT post_id, meta_key, meta_value
FROM wp_postmeta
WHERE meta_key = '_wp_old_slug'
AND meta_value IN (
'comment-lancer-une-boutique-de-reductions-en-ligne',
'how-to-start-an-online-discount-store'
);
Il risultato, però, non spiegava il problema.
Lo slug francese non risultava registrato come _wp_old_slug della pagina inglese.
Inoltre, un vecchio slug WordPress avrebbe normalmente dovuto portare a un redirect verso il nuovo permalink, non necessariamente al caricamento diretto della pagina con status HTTP 200.
Era quindi necessario continuare a cercare.
Terzo tentativo: verificare eventuali contenuti duplicati nel database
Un’altra possibilità era l’esistenza di due pagine distinte che apparentemente visualizzavano lo stesso contenuto.
Per verificarlo è possibile interrogare wp_posts:
SELECT
ID,
post_title,
post_name,
post_status,
post_type
FROM wp_posts
WHERE post_name IN (
'comment-lancer-une-boutique-de-reductions-en-ligne',
'how-to-start-an-online-discount-store'
);
In un sito WPML è utile controllare anche la relazione tra le traduzioni.
Ad esempio:
SELECT
p.ID,
p.post_title,
p.post_name,
t.language_code,
t.source_language_code,
t.trid
FROM wp_posts p
INNER JOIN wp_icl_translations t
ON t.element_id = p.ID
AND t.element_type = CONCAT('post_', p.post_type)
WHERE p.post_name IN (
'comment-lancer-une-boutique-de-reductions-en-ligne',
'how-to-start-an-online-discount-store'
)
ORDER BY t.trid, t.language_code;
Il risultato mostrava correttamente due pagine appartenenti allo stesso gruppo di traduzione WPML:
FR comment-lancer-une-boutique-de-reductions-en-ligne
EN how-to-start-an-online-discount-store
Nessuna pagina inglese utilizzava lo slug francese.
Anche questa ipotesi poteva quindi essere esclusa.
Ho sospettato che WPML conservasse lo slug da qualche altra parte
A questo punto il sospetto si è spostato su WPML.
La domanda era:
WPML potrebbe conservare da qualche parte lo slug originale della traduzione e continuare a utilizzarlo per risolvere gli URL?
Ho controllato le relazioni WPML e le principali informazioni memorizzate nelle tabelle icl_*, senza però trovare un secondo permalink associato alla pagina inglese.
Ed è qui che il problema iniziava a diventare interessante.
Lo slug apparentemente “fantasma” non era memorizzato sulla pagina inglese.
Il canonical era corretto, ma l’URL sbagliato continuava a rispondere 200
Il canonical forniva però un indizio importante.
L’URL:
/en/comment-lancer-une-boutique-de-reductions-en-ligne/
restituiva la pagina inglese ma dichiarava come canonical:
/en/how-to-start-an-online-discount-store/
Questo significava che il sistema conosceva il permalink corretto.
Il problema era quindi probabilmente nella fase precedente: la risoluzione della richiesta HTTP.
Qualcosa stava dicendo a WordPress:
Anche se l’utente ha richiesto
/pl/comment-lancer-une-boutique-de-reductions-en-ligne/, so quale contenuto vuole vedere.
Il sospetto principale è quindi diventato Permalink Manager.
Il test decisivo: disattivare Permalink Manager
Ho effettuato il test più semplice ma anche quello che ha permesso di circoscrivere definitivamente il problema.
Ho disattivato temporaneamente Permalink Manager e richiesto nuovamente:
/en/comment-lancer-une-boutique-de-reductions-en-ligne/
Questa volta il risultato era:
404 Not Found
Perfetto.
WPML e WordPress, da soli, non consideravano valido quell’URL.
Riattivando Permalink Manager:
/en/comment-lancer-une-boutique-de-reductions-en-ligne/
tornava invece a rispondere:
200 OK
A questo punto il responsabile era praticamente individuato.
Ma Permalink Manager non conteneva quello slug
Qui arrivava il secondo aspetto particolare del problema.
Permalink Manager salva diverse informazioni all’interno di wp_options, tra cui URI e redirect.
Ho quindi cercato la stringa:
comment-lancer-une-boutique-de-reductions-en-ligne
nelle opzioni del plugin e, più in generale, in wp_options.
Ad esempio:
SELECT option_name, option_value
FROM wp_options
WHERE option_value LIKE '%comment-lancer-une-boutique-de-reductions-en-ligne%';
Ma non risultava nessuna associazione tra quello slug e la pagina inglese.
All’inizio sembrava un controsenso:
- Permalink Manager era sicuramente responsabile;
- disattivandolo l’URL diventava 404;
- ma lo slug incriminato non risultava salvato da Permalink Manager.
La spiegazione era che Permalink Manager non stava leggendo un secondo URL dal database: lo stava ricostruendo dinamicamente sfruttando la relazione tra le lingue WPML.
La vera causa: “WPML/Polylang fix language mismatch”
Nelle impostazioni di Permalink Manager è presente una funzione dedicata alla compatibilità con i plugin multilingua:
WPML/Polylang fix language mismatch
A seconda della versione del plugin, questa impostazione può prevedere comportamenti equivalenti a:
Disable
Load the language variant of the requested page
oppure:
Redirect to the language variant of the requested page
Nel mio caso era attiva la modalità che caricava la variante linguistica corrispondente alla pagina richiesta.
Ed era esattamente questo a generare il comportamento anomalo.
Cosa accadeva realmente
Supponiamo di avere queste due traduzioni:
Francese
/fr/comment-lancer-une-boutique-de-reductions-en-ligne/
Inglese
/en/how-to-start-an-online-discount-store/
Quando veniva richiesto:
/en/comment-lancer-une-boutique-de-reductions-en-ligne/
Permalink Manager era in grado di riconoscere lo slug:
comment-lancer-une-boutique-de-reductions-en-ligne
come appartenente alla pagina francese.
Contemporaneamente rilevava che l’URL richiesto utilizzava la lingua inglese:
/en/
Grazie all’integrazione con WPML poteva quindi ragionare in questo modo:
comment-lancer-une-boutique-de-reductions-en-ligne
↓
pagina francese
↓
WPML
↓
trova la traduzione inglese
↓
carica la pagina inglese
Il risultato finale era:
/en/comment-lancer-une-boutique-de-reductions-en-ligne/
↓
200 OK
↓
contenuto della pagina inglese
Ecco perché non esisteva nessuno “slug fantasma” nel database.
L’URL veniva interpretato dinamicamente.
Il comportamento è coerente con il funzionamento di Permalink Manager, che individua i contenuti utilizzando l’URI completo e non si affida esclusivamente alle normali rewrite rules di WordPress.
Perché il Canonical Redirect non risolveva il problema
Un’altra cosa che inizialmente poteva trarre in inganno era che nelle impostazioni di Permalink Manager risultava già attivo:
Canonical redirect
Mi sarei quindi aspettata:
/en/comment-lancer-une-boutique-de-reductions-en-ligne/
↓
301
↓
/en/how-to-start-an-online-discount-store/
Invece ricevevo un 200.
La spiegazione è che “Canonical redirect” e “WPML/Polylang fix language mismatch” gestiscono due aspetti diversi della richiesta.
Il plugin può identificare la pagina appartenente a un’altra lingua e sostituirla con la relativa traduzione prima che il normale meccanismo di canonicalizzazione produca il risultato che ci aspetteremmo.
Il codice corrente di Permalink Manager mostra infatti sia l’opzione canonical_redirect sia l’opzione separata fix_language_mismatch, confermando che si tratta di due impostazioni indipendenti.
La soluzione
La soluzione è stata modificare:
Permalink Manager → Settings → WPML/Polylang fix language mismatch
passando dal caricamento diretto della variante linguistica alla modalità:
Redirect to the language variant of the requested page
In questo modo, richiedendo:
/en/comment-lancer-une-boutique-de-reductions-en-ligne/
il comportamento diventa:
HTTP 301
Location: /en/how-to-start-an-online-discount-store/
e infine:
/en/how-to-start-an-online-discount-store/
HTTP 200
Esattamente ciò che volevo ottenere.
In alternativa è possibile disattivare completamente il sistema di correzione del language mismatch, facendo sì che una combinazione non valida tra lingua e slug restituisca un errore.
Nel mio caso ho preferito il redirect perché l’URL errato poteva essere già stato scoperto o indicizzato dai motori di ricerca.
Come verificare che la correzione funzioni
Dopo aver modificato l’impostazione consiglio di:
- salvare nuovamente i permalink da Impostazioni → Permalink;
- svuotare eventuali cache WordPress;
- svuotare la cache del server o della CDN;
- verificare nuovamente gli URL.
Da terminale si può effettuare un controllo molto semplice:
curl -I https://www.example.com/en/comment-lancer-une-boutique-de-reductions-en-ligne/
Prima della modifica il risultato era simile a:
HTTP/2 200
Dopo la correzione dobbiamo invece trovare:
HTTP/2 301
location: https://www.example.com/en/how-to-start-an-online-discount-store/
Il secondo URL deve poi restituire normalmente:
HTTP/2 200
Canonical e redirect non sono la stessa cosa
Questa esperienza è anche un buon esempio di un concetto SEO che spesso genera confusione.
Avere:
<link rel="canonical" href="URL-CORRETTO">
su un URL duplicato non equivale a effettuare un redirect.
Il canonical è un’indicazione fornita ai motori di ricerca.
Il redirect HTTP, invece, impedisce effettivamente al browser e al crawler di considerare quella risorsa come una pagina autonoma.
Nel mio caso avevo:
URL errato
HTTP 200
Canonical:
URL corretto
La situazione era quindi migliore rispetto ad avere due canonical autoreferenziali, ma continuavo comunque ad avere due URL navigabili per lo stesso contenuto.
La soluzione più pulita era:
URL errato
301
↓
URL corretto
200
Una checklist per problemi simili con WPML e Permalink Manager
Se su un sito WordPress multilingua due URL diversi mostrano lo stesso contenuto, consiglio di controllare nell’ordine:
1. Lo slug nativo WordPress
Verificare post_name in wp_posts.
2. Il permalink personalizzato
Controllare l’URI visualizzato da Permalink Manager.
3. _wp_old_slug
Cercare eventuali vecchi slug memorizzati da WordPress.
4. Le traduzioni WPML
Verificare wp_icl_translations, soprattutto:
element_id
language_code
trid
per essere sicuri che non esistano traduzioni duplicate o relazioni errate.
5. I dati di Permalink Manager
Controllare eventualmente:
permalink-manager
permalink-manager-uris
permalink-manager-redirects
permalink-manager-external-redirects
all’interno di wp_options.
Il codice corrente del plugin conferma che queste informazioni vengono caricate da specifiche option WordPress.
6. Disattivare temporaneamente Permalink Manager
È il test che, nel mio caso, ha permesso di individuare immediatamente il responsabile.
Se:
Permalink Manager attivo → 200
Permalink Manager spento → 404
conviene smettere di cercare uno slug duplicato dentro WPML e concentrarsi sulla logica di routing di Permalink Manager.
7. Controllare “WPML/Polylang fix language mismatch”
Questo è stato il punto decisivo nel mio caso.
Conclusione
Il problema sembrava inizialmente causato da uno slug duplicato salvato da WPML o da Permalink Manager.
In realtà non esisteva nessun secondo permalink associato alla pagina inglese.
Era Permalink Manager che, riconoscendo lo slug appartenente alla pagina francese e trovandosi all’interno della directory /pl/, utilizzava WPML per recuperare automaticamente la relativa traduzione inglese.
Per questo:
/en/comment-lancer-une-boutique-de-reductions-en-ligne/
riusciva a mostrare:
/en/how-to-start-an-online-discount-store/
pur mantenendo il primo URL nella barra del browser.
L’impostazione che ha risolto definitivamente il problema è stata:
WPML/Polylang fix language mismatch → Redirect to the language variant of the requested page
Il caso dimostra anche quanto sia importante, quando si analizzano problemi di permalink su WordPress, non limitarsi a cercare URL e slug nel database.
Plugin come Permalink Manager possono infatti risolvere dinamicamente una richiesta e modificare il contenuto caricato in base alla lingua, rendendo apparentemente valido un URL che in WordPress, tecnicamente, non esiste.
FAQ
Perché WPML mostra la stessa pagina con due slug diversi?
Non è necessariamente WPML a memorizzare due slug. Se è presente Permalink Manager, il plugin può riconoscere uno slug appartenente a un’altra lingua e utilizzare WPML per caricare la traduzione corrispondente.
Perché non trovo il secondo slug nel database?
Perché l’URL potrebbe non essere memorizzato. Permalink Manager può interpretarlo dinamicamente durante la richiesta.
Perché entrambi gli URL restituiscono HTTP 200?
Se Permalink Manager è configurato per caricare automaticamente la variante linguistica della pagina richiesta, può trasformare internamente una richiesta relativa a una lingua nella traduzione corretta senza modificare l’URL nel browser.
Il canonical è sufficiente per evitare contenuti duplicati?
Il canonical aiuta i motori di ricerca a identificare l’URL preferito, ma quando un URL alternativo non deve realmente essere accessibile è generalmente più pulito utilizzare un redirect permanente verso quello corretto.
Quale impostazione di Permalink Manager bisogna controllare?
In presenza di WPML o Polylang bisogna verificare soprattutto l’opzione:
WPML/Polylang fix language mismatch
Se un URL con lo slug di un’altra lingua viene caricato con status 200, è particolarmente importante controllare se è impostata la modalità che carica direttamente la variante linguistica.
Come posso capire velocemente se il problema è Permalink Manager?
Disattiva temporaneamente il plugin e prova nuovamente l’URL anomalo.
Se passa da:
HTTP 200
a:
HTTP 404
hai un’indicazione molto forte che la risoluzione alternativa dell’URL dipenda da Permalink Manager.