Правило mod_rewrite для обеспечения канонического процентного кодирования

У нас есть приложение PHP с динамической схемой URL, которая требует, чтобы символы были даже в процентахнезарезервированные персонажи"как скобки или апострофы, которые на самом деле не требуется кодировать. URL-адреса, которые приложение считает закодированным" неправильным "способом, канонизируются и затем перенаправляются в" правильное "кодирование.

Но Google и другие пользовательские агенты могут по-разному канонизировать процентное кодирование / декодирование. Это означает, что когда робот Google запрашивает страницу, он запрашивает «неправильный» URL, а когда он возвращает перенаправление на «правильный» URL, робот Google отказывается следовать редирект и откажется проиндексировать страницу.

Да, это ошибка с нашей стороны. Спецификации HTTP требуют, чтобы серверы обрабатывали процентно-не кодированные процентами незарезервированные символы одинаково. Но исправить проблему в коде приложения прямо сейчас непросто, поэтому я надеялся избежать изменения кода с помощью правила перезаписи Apache, которое обеспечило бы правильное кодирование URL-адресов с точки зрения приложения. Это означает, что все апопстры, скобки и т. д. закодированы в процентах, а пробелы - в виде+ и не%20.

Вот один пример, где я хочу переписать первый и в конечном итоге второй формы:

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

Вот еще один:

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

Вот еще один:

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

Если приложение увидит только вторую форму этих URL-адресов, оно не будет отправлять никаких перенаправлений, и Google сможет проиндексировать страницу.

Я новичок с правилами переписывания, и это было ясно из моего прочтениямод-переписать документацию этот mod_rewrite выполняет некоторое автоматическое кодирование / декодирование, которое может помочь или навредить тому, что я хочу сделать, хотя и не уверен.

Какой-нибудь совет для правил переписывания, чтобы обращаться с вышеупомянутыми случаями? Я согласен с правилом для каждого специального символа, так как их не так много, но одно правило (если возможно) будет идеальным.

 Justin Grant27 сент. 2010 г., 19:49
у нас есть полный доступ к конфигурации Apache, хотя я подозреваю, что наша команда ops предпочла бы минимально инвазивное решение.
 Tim Stone26 сент. 2010 г., 06:41
У вас есть полный доступ к конфигурации Apache или вы ограничены .htaccess-дружественным решением?

Ответы на вопрос(2)

Решение Вопроса

хотя оно будет работать только в Apache 2.2 и более поздних версиях из-за использованияB флаг, Я не уверен, правильно ли он заботится о каждом случае (по общему признанию, я немного скептически отношусь к тому, что это не требует больше работы, чем эта), но я уверен, что это следует из исходного кода.

Имейте также в виду, что ценностьREQUEST_URI не обновляется преобразованиями mod_rewrite, поэтому, если ваше приложение использует это значение для определения запрошенного URL-адреса, сделанные вами изменения в любом случае не будут видны.

Хорошей новостью является то, что это можно сделать в .htaccess, поэтому у вас есть возможность оставить основную конфигурацию без изменений, если она работает лучше для вас.

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]

Итак, почему есть необходимость использоватьB флаг вместо того, чтобы позволить mod_rewrite экранировать переписанный URL автоматически? Когда mod_rewrite автоматически экранирует URL, он используетap_escape_uri (который, по-видимому, был превращен в макрос дляap_os_escape_path по какой-то причине ...), функция, которая избегает ограниченного набора символов.B флаг, однако, использует функцию внутреннего модуля под названиемescape_uri, который смоделирован на PHPurlencode функция.

Реализацияescape_uri в модуле предполагается, что буквенно-цифровые символы и подчеркивания оставляются как есть, пробелы преобразуются в +, а все остальное преобразуется в его экранированный эквивалент. Похоже, это поведение, которое вы хотите, так что, вероятно, оно должно работать.

Если нет, у вас есть возможность настроить внешнюю программуRewriteMap это может манипулировать ваши входящие URL-адреса в правильном формате. Однако это требует манипулирования конфигурацией Apache, а скрипт-отступник может вызвать проблемы для сервера в целом, поэтому я не считаю его идеальным решением, если его можно избежать.

 Justin Grant30 сент. 2010 г., 18:14
Это похоже на ответ, который я искал. Ницца!

rewrite вы можете заменить только фиксированное количество вхождений за раз. Но это возможно:

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]

Это заменит один%20, ', (, или же) за один раз и отвечает 301 перенаправлением. Таким образом, если путь URL содержит 10 символов, которые необходимо заменить, для этого требуется 10 перенаправлений.

Поскольку это может быть не самым лучшим решением, можно выполнить все замены, кроме последней внутренней, используяN флаг и только последняя замена внешне с редиректом:

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]

Но используяN Флаг может быть опасным, поскольку он не увеличивает внутренний счетчик рекурсии и, следовательно, может легко привести к бесконечной рекурсии.

 Justin Grant30 сент. 2010 г., 18:18
Хммм. На уровне сложности выше, вероятно, проще попросить команду разработчиков переписать свой код перенаправления. :-) Другой ответ выглядит проще, поэтому я приму его. Но мне нравится ваша общая идея повторения правил - это может быть не то решение, которое я хотел бы здесь, но может быть полезным в других обстоятельствах. Спасибо! +1
 Gumbo30 сент. 2010 г., 20:39
@ Джастин Грант: Да, наверное.

Ваш ответ на вопрос