Внешний (301) редирект выполняет не Apache, а modx (судя по всему, анализирует содержимое полей глобальной переменной $_SERVER — иначе как modx узнает, что на уровне веб-сервера был изменён URI ресурса, который ему втюхали) Всё так и есть. При включенных дружественных URL и включенной опции (friendly_urls_strict) в методе (modRequest::_cleanResourceIdentifier()) выполняется внешний (301) редирект с внешнего URI (запрошенного со стороны браузера) на внутренний URI того ресурса, который должен отображаться: /**
Cleans the resource identifier from the request params.
@param string $identifier The raw identifier.
@return string|integer The cleansed identifier.
*/
public function _cleanResourceIdentifier($identifier) {
if (empty ($identifier)) {
...
}
elseif ($this->modx->getOption('friendly_urls', null, false) && $this->modx->resourceMethod == 'alias') {
$containerSuffix = trim($this->modx->getOption('container_suffix', null, ''));
$found = $this->modx->findResource($identifier);
if ($found === false && !empty ($containerSuffix)) {
...
} elseif ((integer) $this->modx->getOption('site_start', null, 1) === $found) {
...
} else {
if ($this->modx->getOption('friendly_urls_strict', null, false)) {
$requestUri = $_SERVER['REQUEST_URI'];
$qsPos = strpos($requestUri, '?');
if ($qsPos !== false) $requestUri = substr($requestUri, 0, $qsPos);
$fullId = $this->modx->getOption('base_url', null, MODX_BASE_URL) . $identifier;
$requestUri = urldecode($requestUri);
if ($fullId !== $requestUri && strpos($requestUri, $fullId) !== 0) {
$parameters = $this->getParameters();
unset($parameters[$this->modx->getOption('request_param_alias')]);
$url = $this->modx->makeUrl($found, $this->modx->context->get('key'), $parameters, 'full');
$this->modx->sendRedirect($url, array('responseCode' => 'HTTP/1.1 301 Moved Permanently'));
}
}
...
}
} else {
...
}
return $identifier;
} Это штатные действия modx, которые реализуют логику «строгих» (канонических) URL при включении опции (friendly_urls_strict): Если выбрано «Да», неканонические запросы, соответствующие ресурсу, будут перенаправлены с кодом 301 на канонический URI для этого ресурса. WARNING: Do not enable if you use custom rewrite rules which do not match at least the beginning of the canonical URI. For example, a canonical URI of foo/ with custom rewrites for foo/bar.html would work, but attempts to rewrite bar/foo.html as foo/ would force a redirect to foo/ with this option enabled. — Поэтому, если одновременно необходимо:
а) исключить URI-дубли страниц включением опции (friendly_urls_strict) (необходимо для SEO)
б) на уровне веб-сервера выполнять внутренний редирект с одного ресурса на другой то нужно будет выполнить следующие действия:
Включить опцию (friendly_urls_strict)
Прописать требуемые директивы для редиректа в настройках веб-сервера (или в .htaccess) 3. В плагине на событии (OnHandleRequest) (последнее событие, которое генерируется до вызова метода modRequest::_cleanResourceIdentifier()) установить эту опцию в значение (false) для тех исходных URL, для которых выполняется внутренний редирект на уровне веб-сервера (выявить такие ситуации можно сравнением значения глобальной переменной $_SERVER['REQUEST_URI'] и значением переменной запроса q ($modx->getOption('request_param_alias')) — именно так и делает метод modRequest::_cleanResourceIdentifier()) 4. (НЕ ОБЯЗАТЕЛЬНО) В плагине на событии (OnWebPageInit) (первое событие, которое генерируется после вызова метода modRequest::_cleanResourceIdentifier()) установить эту опцию в исходное значение (исходное значение при этом нужно запомнить в предыдущем плагине). P.S. При таком подходе в тех случаях, когда выполняется внутренний редирект на уровне веб-сервера, контролировать уникальность внешних URL придётся самому (поскольку для этих случаев описанными выше действиями мы отключаем такой контроль со стороны modx).