1. Внешний (301) редирект выполняет не Apache, а m

Nov 15, 2015
  1. Внешний (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) б) на уровне веб-сервера выполнять внутренний редирект с одного ресурса на другой

то нужно будет выполнить следующие действия:

  1. Включить опцию (friendly_urls_strict)
  2. Прописать требуемые директивы для редиректа в настройках веб-сервера (или в .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).