Удаляем пустую строку запроса из URL

Порой URL-адрес оканчивается нежелательным вопросительным знаком: например, /example/? вместо /example/. Вопросительный знак отделяет основную часть адреса от строки запроса (query string), но некоторые браузеры добавляют его, даже если GET-форма, результатом отправки которой является такой адрес, не содержит полей и, соответственно, строка запроса пуста.

Это происходит, например, при исполь­зовании кнопок-ссылок в браузерах на основе движков Chromium / Blink (Chrome, Opera 15+, Яндекс.Браузер, Vivaldi) и WebKit (Safari) из-за имеющейся в них ошибки (1, 2). Авто­мати­чески удалить вопросительный знак при пустой строке запроса можно с помощью серверного пере­направления (редиректа).

PHP

Наиболее просто задача решается на серверном скриптовом языке PHP. Полный запрошенный путь обычно хранится в серверной переменной REQUEST_URI. Если первый вопросительный знак в значении этой переменной является её последним символом, следует перенаправлять на адрес без вопросительного знака.

$uri  = $_SERVER['REQUEST_URI'];
$qPos = strpos($uri, '?');

if ($qPos === strlen($uri) - 1) {
    header('HTTP/1.1 301 Moved Permanently');
    header('Location: ' . substr($uri, 0, $qPos));
    exit;
}

Этот код следует вставить в индексный файл вашего сайта перед любым другим PHP-кодом. Если не хочется изменять комплектные файлы CMS (например, для возможности её обновления), можно задать собственный индексный файл (например, custom-index.php), внутрь которого поместить данный код, после которого с помощью require_once подключить истинный индексный файл CMS — обычно это index.php.

Apache / .htaccess

Собственными средствами популярного веб-сервера Apache несколько проблематично надёжное различение ситуаций, когда строка запроса есть, но пустая (есть только вопросительный знак в конце URL-адреса), и когда строка запроса отсутствует полностью (URL-адрес не содержит даже вопросительного знака).

В серверную переменную REQUEST_URI в Apache (в отличие от PHP) вопросительный знак при пустой строке запроса, похоже, вообще не попадает.

Остаётся анализировать серверную переменную THE_REQUEST, содержащую не только запрошенный URL-адрес, а весь HTTP-запрос вида GET uri HTTP/1.1:

RewriteEngine On
RewriteCond %{THE_REQUEST} ^[^\s]+\s+[^?]*?\?
RewriteCond %{QUERY_STRING} =""
# Для любых версий Apache:
RewriteRule .? %{REQUEST_URI}? [R=301,L]
# Для Apache 2.4+:
# RewriteRule .? %{REQUEST_URI} [R=301,L,QSD]

Метод запроса (например, GET) и протокол (HTTP/1.1) вопросительных знаков обычно не содержат, поэтому возможна более простая (хотя теоретически несколько менее надёжная) проверка значения переменной THE_REQUEST на наличие вопросительного знака в любой части запроса, а не только в URL-адресе:

RewriteCond %{THE_REQUEST} \?