2010-09-24 32 views
2

Nous avons une application PHP avec un schéma d'URL dynamique qui exige que les caractères soient codés en pourcentage, même "unreserved characters" comme des parenthèses ou des aphostrophes qui ne doivent pas être codés. Les URL que l'application considère comme étant «fausses» sont canonisées, puis redirigées vers le «bon» encodage. Mais Google et les autres agents utilisateurs vont canaliser le codage/décodage en pourcentage différemment, ce qui signifie que lorsque Googlebot demandera à la page de demander la "mauvaise" URL, et quand il récupérera une redirection vers l'URL "correcte", Googlebot refusera de suivre la redirection et refusera d'indexer la page.Règle mod_rewrite pour appliquer le codage en pourcentage canonique

Oui, c'est un bug de notre côté. Les spécifications HTTP requièrent que les serveurs traitent les caractères non réservés codés en pourcentage et non codés en pourcentage de manière identique. Mais corriger le problème dans le code de l'application n'est pas simple en ce moment, donc j'espérais éviter un changement de code en utilisant une règle de réécriture Apache qui assurerait que les URL sont encodées "correctement" du point de vue de l'application , ce qui signifie que les apopstrophes, les parenthèses, etc. sont tous codés en pourcentage et que les espaces sont codés comme + et non %20.

Voici un exemple, où je veux réécrire la première et se retrouver avec la deuxième forme:

  • www.splunkbase.com/apps/All/4.x/Add-On/app:OPSEC + LEA + pour + Check + point + (Linux)
  • www.splunkbase.com/apps/All/4.x/Add-On/app:OPSEC+LEA+for+Check+Point+%28Linux%29

Voici une autre:

  • www.splunkbase.com/apps/All/4.x/app:Benford's+Law+Fraud+Detection+Add-on
  • www.splunkbase.com/apps/All/4.x/app:Benford%27s + droit + fraude + détection + Add-on

Voici une autre:

  • www.splunkbase.com/apps/All/4.x/app:Benford%27s%20Law%20Fraud%20Detection % 20Add sur
  • www.splunkbase.com/apps/All/4.x/app:Benford%27s+Law+Fraud+Detection+Add-on

Si l'application ne voit que la deuxième forme de ces URL, elle n'enverra aucune redirection et Google pourra indexer la page. Je suis un débutant avec les règles de réécriture, et il était clair à partir de ma lecture de la mod-rewrite documentation que mod_rewrite fait un codage/décodage automatique qui peut aider ou blesser ce que je veux faire, mais je ne suis pas sûr.

Un conseil pour les règles de réécriture pour gérer les cas ci-dessus? Je suis OK avec une règle pour chaque caractère spécial car il n'y en a pas beaucoup, mais une seule règle (si possible) serait idéale.

+1

Avez-vous un accès complet à la configuration Apache, ou êtes-vous limité à une solution .htaccess-friendly? –

+0

nous avons un accès complet à apache config, bien que je soupçonne que notre équipe ops préférerait une solution peu invasive. –

Répondre

1

La solution peut en fait être assez simple, bien qu'elle ne fonctionne que dans Apache 2.2 et plus tard en raison de l'utilisation du B flag. Je ne suis pas sûr si cela prend soin de tous les cas correctement (je suis un peu sceptique, il n'implique pas plus de travail que cela), mais je suis amené à le croire par le code source. Notez également que la valeur REQUEST_URI n'est pas mise à jour par les transformations mod_rewrite. Par conséquent, si votre application utilise cette valeur pour déterminer l'URL demandée, les modifications apportées ne seront pas visibles. La bonne nouvelle est que cela peut être fait dans .htaccess, donc vous avez la possibilité de laisser la configuration principale intacte si cela fonctionne mieux pour vous.

RewriteEngine On 

# Make sure this is only done once to avoid escaping the escapes... 
RewriteCond %{ENV:REDIRECT_STATUS} ^$ 
# Check if we have anything to bother escaping (likely unnecessary...) 
RewriteCond $0 [^\w]+ 
# Rewrite the entire URL by escaping the backreference 
RewriteRule ^.*$ $0 [B] 

Alors, pourquoi est-il nécessaire d'utiliser le drapeau B au lieu de laisser échapper mod_rewrite automatiquement l'URL réécrite? Lorsque mod_rewrite échappe automatiquement à l'URL, il utilise ap_escape_uri (qui a apparemment été transformé en une macro pour ap_os_escape_path pour une raison quelconque ...), une fonction qui échappe à un sous-ensemble limité de caractères. Le drapeau B, cependant, utilise une fonction de module interne appelée escape_uri, qui est modélisée sur la fonction urlencode de PHP. L'implémentation de escape_uri dans le module suggère que les caractères alphanumériques et les traits de soulignement sont laissés tels quels, les espaces sont convertis en + et tout le reste est converti en son équivalent échappé. Cela semble être le comportement que vous voulez, alors probablement que cela devrait fonctionner. Si ce n'est pas le cas, vous avez la possibilité de configurer un programme externe RewriteMap qui pourrait manipuler vos URL entrantes dans le bon format. Cela nécessite de manipuler la configuration d'Apache, et un script renégat pourrait causer des problèmes au serveur dans son ensemble, donc je ne considère pas que ce soit une solution idéale si cela peut être évité.

+0

Cela ressemble à la réponse que je cherchais. Agréable! –

1

mod_rewrite n'est pas le meilleur outil pour faire ce genre de travail. Parce qu'avec mod_rewrite vous ne pouvez remplacer qu'un nombre fixe d'occurrences à la fois.Mais il est possible:

RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?\ ]*)%20([^?\ ]*) 
RewriteRule^/%1+%2 [R=301,NE] 
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?'\ ]*)'([^?'\ ]*) 
RewriteRule^/%1\%27%2 [R=301,NE] 
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?(\ ]*)\(([^?(\ ]*) 
RewriteRule^/%1\%28%2 [R=301,NE] 
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?)\ ]*)\)([^?)\ ]*) 
RewriteRule^/%1\%29%2 [R=301,NE] 

Cela remplacera une %20, ', ( ou ) à un moment et répond avec une redirection 301. Donc, si un chemin d'URL contient 10 caractères qui doivent être remplacés, il a besoin de 10 redirections pour le faire.

Comme cela pourrait ne pas être la meilleure solution, il est possible de faire tous les remplacements sauf le dernier à l'aide interne du N flag et seul le dernier remplacement de l'extérieur avec une redirection:

RewriteCond %{THE_REQUEST} ^[A-Z]+\ /(([^?%\ ]|%(2[1-9a-fA-F]|[013-9][0-9a-fA-F]))*)%20(([^?%\ ]|%(2[1-9a-fA-F]|[013-9][0-9a-fA-F]))*%20[^?\ ]*) 
RewriteRule^/%1+%4 [R=301,NE] 
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?\ ]*)%20([^?\ ]*)[?\ ] 
RewriteRule^/%1+%2 [R=301,NE] 
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?'\ ]*)'([^?'\ ]*'[^?\ ]*) 
RewriteRule^/%1\%27%2 [N,NE] 
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?'\ ]*)'([^?'\ ]*)[?\ ] 
RewriteRule^/%1\%27%2 [R=301,NE] 
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?(\ ]*)\(([^?(\ ]*\([^?\ ]*) 
RewriteRule^/%1\%28%2 [N,NE] 
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?(\ ]*)\(([^?(\ ]*)[?\ ] 
RewriteRule^/%1\%28%2 [R=301,NE] 
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?)\ ]*)\)([^?)\ ]*\)[^?\ ]*) 
RewriteRule^/%1\%29%2 [N,NE] 
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^?)\ ]*)\)([^?)\ ]*)[?\ ] 
RewriteRule^/%1\%29%2 [R=301,NE] 

mais en utilisant le N Le flag peut être dangereux car il n'incrémente pas le compteur de récursion interne et peut donc facilement conduire à une récursion infinie.

+0

Hmmm. Au niveau de complexité ci-dessus, il est probablement plus facile de demander à l'équipe de développement de réécrire son code de redirection. :-) L'autre réponse semble être plus simple, donc je vais l'accepter. Mais j'aime votre idée générale de répéter les règles - ce n'est peut-être pas la solution que je voudrais, mais cela peut être utile dans d'autres circonstances. Merci! +1 –

+0

@Justin Grant: Oui, probablement. – Gumbo