In una precedente discussione di questo forum, si son riscontrati vari problemi su un sito in esame...
prima rigby76 ha fatto notare che nella google-cache c'erano pagine del tipo
1) example.com/index.php/
poi seo_sem ha notato altri url che davano status 200 del tipo
2) example.com/////////index.php
infine io ho visto che addirittura c'era lo status 200 su cose del tipo
3) example.com/pagina.php/quellochevuoi
Girando un po' sul web ho visto che il problema è molto diffuso, persino su siti per seo e webmaster... googlando un po' ho potuto avere l'impressione che, di questi problemi, i tipi 1 e 3 sopra in esempio non son stati mai affrontati in questi termini (duplicazione di url), mentre del problema 2 son state date parziali soluzioni.
Cerchiamo allora di affrontare prima i tipi 1 e 3 ... la ricerca è stata un po' lunga (2 mezze nottate) perchè vagavo tra potenziali colpevoli (os? httpd? conf?) e tra potenziali moventi (bug? innovazione?)... e soprattutto perchè non volevo mettere pezze col rewrite su ogni host per un problema che secondo me (per logica, oltre che per prestazioni, semplicità e correttezza) andava risolto a livello centrale... veniamo al dunque allora...
il colpevole è apache 2 e il movente una nuova opzione!
Apache ha creato vari problemi di duplicazione url con le sue innovazioni... in particolare vi faccio presente:
- CheckSpelling che restituisce l'output dell'url più simile lessicalmente (es: /contatti.html duplica su /contati.html);
- MultiViews che restituisce file di cui non si è specificata l'estensione (es: /file.html duplica su /file);
- AcceptPathInfo che restituisce il primo path esistente levando man mano le successive sottocartelle (es: /pagina.php duplica su /pagina.php/ciao)
I primi 2 in realtà son presenti dalla versione 1.3 e di solito non danno problemi perchè devono essere specificamente attivati... ma se aveste i problemi descritti vale la pena di cercare righe come:
CheckSpelling On
Options +Multiviews
quindi, se le trovate eliminatele dal httpd.conf
se non avete accesso alla configurazione del server, può essere possibile disattivarle dall'htaccess (se il provider vi ha lasciato almeno l'AllowOverride) con:
CheckSpelling Off
Options -MultiViews
Il terzo è invece il responsabile dei problemi (1 e 3) all'inizio presentati... è stato introdotto con la versione 2 ed è attivo di default... quindi potreste non trovare nessuna riga da cancellare dall'httpd.conf (al contrario di ciò che pensavo io leggendolo e rileggendolo)... ma dovete esplicitamente mettere insieme alla prime direttive:
AcceptPathInfo Off
per fortuna di chi non ha accesso allla configurazione del server, tale istruzione si può mettere anche nell'htaccess.
Occhio che quest'istruzione ha in realtà uno scopo più ampio di quello descritto... è nato per supportare una nuova modalita di scripting a livello di webserver... di fatto ciò che si mette dopo gli slash fasulli vien recepito come parametro... tipo una query string.
Quindi alcuni script basati su questa innovazione (ne ho visto in giro già uno) devono tenerla attivata.
Non ho approfondito il funzionamento di questi script, ma personalmente credo che siano una versione embedded di php... molto meglio quella completa che è più potente e associata al rewrite non crea nemmeno questi problemi.
L'ultima affermazione ha un senso molto pratico... e mostro di seguito una piccola perla di ottimizzazione, perchè a sto punto nasce spontaneo chiedersi se ha senso preoccuparsi di ciò e lasciare che le query string creino arbitrariamente duplicazioni...
beh, a seconda della complessità del sito si hanno 3 soluzioni...
1) se il sito non usa parametri, si da il 404 per ogni query string
if ($_SERVER['QUERY_STRING']<>"") {header("HTTP/1.0 404 Not Found");exit};
2) se il sito utilizza un insieme piccolo di parametri, si crea un array dei parametri
$param = array ("sezione", "id", "etc");
si crea un altro array con ciò che è stato immesso nella query string
$paramreq=preg_split('/=?(.*)&?/', $_SERVER['QUERY_STRING']);
si confrontano e nel caso qualche parametro non sia tra i previsti si restituisce lo status 404
$paramerr=array_diff($paramreq, $param);
if (count($paramerr)==0) {header("HTTP/1.0 404 Not Found");exit};
3) se i parametri son tanti, si usa con pazienza il rewrite sulle categorie di pagine che vogliamo indicizzare, quindi basterà mettere il noindex su tutte le pagine dinamiche
if ($_SERVER['QUERY_STRING']<>"") {$metarobot='<meta name="robots" content="noindex, follow'" />};
dove $metarobot è una variabile (se serve, global) che mettiamo nel template.
Per quanto ho scritto, credo che il problema posto all'inizio, sia molto rilevante, poichè gli url duplicati, volendo, si possono completamente eliminare.
Certo, col rewrite spesso si approfitta del .* per semantizzare le url, mettendo titoli e key enll'url... in quel confronta il rischio di duplicazione col vantaggio della semantizzazione... però c'è appunto un vantaggio (almeno usando coerenza sui link interni), mentre lasciando le cose incontrollate ci son solo svantaggi.
Veniamo finalmente al tipo 2, i "multiple slashes"... qui sembra non esistere una soluzione centrale per farli collassare...
per un semplice motivo ufficiale che si può trovare dalle primissime versioni di apache (che restituiva il 404) alle ultime (che delegano al filesystem dell'os) e ad esempio si può leggere a proposito della direttiva Location nella nota finale sugli slash che cita:
"People may be used to its behavior in the filesystem where multiple adjacent slashes are frequently collapsed to a single slash (i.e., /home///foo is the same as /home/foo). In URL-space this is not necessarily true."
così succede che ad esempio, sul mio server debian che collassa gli slash, l'output vien restituito perchè il file vien trovato (collassando) ma l'url rimane quello richiesto dall'utente, visto che in un altra macchina ad esempio un doppio slash può significare di ripartire dalla root.
Quindi una soluzione centrale in generale non esiste... si può usare il rewrite, consapevoli però che le regole di rewrite non sono di default ereditate dalle cartelle o configurazioni superiori e un inherit esplicito (oltre a essere scomodo) può creare conflitti con le regole preesistenti... quindi va messo preferibilmente nell'htaccess...
in giro sui siti english ci son varie soluzioni, ma a me davano conflitti coi sottodomini e me ne son fatto una che sembra essere ok ovunque (sia per gli slash subito dopo l'hostname che per quelli nella request, mentre in giro si trovavano solo insiemi di 2 regole per le 2 situazioni)... ecco la regola
RewriteCond %{THE_REQUEST} ^[[:alpha:]]*\ (.*)/{2,}(.*)\ HTTP(.*)$
RewriteRule .* http://%{HTTP_HOST}%1/%2 [R=301,L]
che può essere copiata e incollata subito dopo il RewriteEngine On (in ogni sottodirectory però).
Per completare la mini guida cito 2 ulteriori problemi tipici:
1) lo slash finale in una directory, che ci sia o no, la pagina è la stessa... beh, sul mio server questo problema non c'è perchè già redirige sempre nella versione con lo slash finale, comunque vi posto una soluzione non testata da me, che potete trovare cercando in english "add trailing slash"
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule ^(.*)$ http://%{HTTP_HOST}/$1/ [L,R=301]
2) eliminare il file indice, in particolare nell'esempio seguente si fa con successo un 301 dalla /index.php alla / (anche qui ho dovuto correggere la versione circolante per non dare problemi con le regole di rewrite preesistenti)
RewriteCond %{IS_SUBREQ} false
RewriteCond %{QUERY_STRING} ^$
RewriteRule ^(.*)index\.php$ http://%{HTTP_HOST}$1 [R=301,L]
Ricordiamo però che nessun redirect può sostituire in pieno il vantaggio di una struttura ragionata e coerente dei link interni.
Naturalmente in tutti questi esempi ho supposto usaste la porta 80...
se usate ssl per qualche sito, le ultime regole vanno duplicate, premettendo la condizione
RewriteCond %{HTTPS} on
e mettendo la s nel prefisso http del redirect.
Benissimo, credo di aver affrontato i principali rischi di duplicazione (e di aver risposto in pieno alla discussione che ha originato questa ricerca). Se qualcuno ne conosce altri, con relativi riferimenti e soluzioni, lo invito a postarli in risposta.