Перенаправить все URL на другой (внешний) сайт, кроме одного пути?

Вопрос или проблема

У меня есть работающий сайт (http://www.example.com), который (использует PHP-FPM), для которого я хочу перенаправить все входящие запросы на URL другого сайта (https://www.other-example.com/foo.html), за исключением одного конкретного пути (/api).

Вот конфигурация Nginx, которую я пробовал (поместил обработку PHP в отдельный location для /api и return 301 на location /):

upstream php {
    server unix:/var/run/php/php7.4-fpm.sock;
}

server {
    listen 80;
    listen [::]:80;

    server_name www.example.com;

    root /var/www/www.example.com;
    index index.php;

    location  ~ ^/api(?:/(.*))?$ {
        #return 418;
        try_files $uri $uri/ /index.php$is_args$args;
    
        location ~ \.php$ {
            #include snippets/fastcgi-php.conf;

            # regex to split $uri to $fastcgi_script_name и $fastcgi_path
            fastcgi_split_path_info ^(.+?\.php)(/.*)$;

            # Проверьте, что PHP-скрипт существует перед передачей его
            try_files $fastcgi_script_name =404;

            # Обойти тот факт, что try_files сбрасывает $fastcgi_path_info
            # см.: http://trac.nginx.org/nginx/ticket/321
            set $path_info $fastcgi_path_info;
            fastcgi_param PATH_INFO $path_info;

            fastcgi_index index.php;
            include fastcgi.conf;
            include fastcgi_params;

            fastcgi_pass php;
        }
    }

    location / {
        return 301 https://www.other-example.com/foo.html;
    }
}

Но все мои запросы (http://www.example.com, http://www.example.com/foo и http://www.example.com/api) получают HTTP/1.1 301 Moved Permanently с Location: https://www.other-example.com/foo.html.

Я знаю, что location ~ ^/api(?:/(.*))?$ работает, потому что если я раскомментирую return 418;, я получаю ответы 418 для запросов http://www.example.com/api, но 301 для остальных запросов.

Обработка nginx осуществляется следующим образом в вашем случае:

  1. Запрос к /api/test_endpoint воспринимается блоком location ~ ^/api(?:/(.*))?$.
  2. В try_files он соответствует части /index.php$is_args$args. Поскольку это последняя часть, это вызывает внутреннее перенаправление.
  3. nginx перезапускает обработку с uri /index.php. Это совпадает с блоком location /, что вызывает перенаправление.

Одним из решений для устранения проблемы является следующая конфигурация:

location  ~ ^/api(?:/(.*))?$ {
    #return 418;
    try_files $uri $uri/ /index.php$is_args$args;
}

location / {
    return 301 https://www.other-example.com/foo.html;
}

location = /index.php {
    internal;
    #include snippets/fastcgi-php.conf;

    # regex to split $uri to $fastcgi_script_name и $fastcgi_path
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;

    # Проверьте, что PHP-скрипт существует перед передачей его
    try_files $fastcgi_script_name =404;

    # Обойти тот факт, что try_files сбрасывает $fastcgi_path_info
    # см.: http://trac.nginx.org/nginx/ticket/321
    set $path_info $fastcgi_path_info;
    fastcgi_param PATH_INFO $path_info;

    fastcgi_index index.php;
    include fastcgi.conf;
    include fastcgi_params;

    fastcgi_pass php;
}

С этой конфигурацией nginx находит соответствие для index.php на шаге 3 обработки запроса. Ключевое слово internal предотвращает внешние запросы к /index.php от соответствия этому блоку. Внешние запросы к /index.php будут обслуживаться блоком location /.

Ответ или решение

Для решения задачи по редиректу всех URL-адресов на внешний сайт, за исключением одного конкретного пути, необходимо правильно настроить конфигурацию Nginx. На основе предоставленного вами кода и описания проблемы, можно выделить несколько ключевых моментов для корректной работы редиректов и обработки запросов.

Шаги для настройки конфигурации Nginx

  1. Обработка запросов к /api
    Для того чтобы запросы к пути /api не перенаправлялись на внешний сайт, добавим отдельный блок location для обработки этих запросов. Ваша конфигурация уже содержит это, но в ней необходимо учесть несколько моментов.

  2. Избежание внутренних редиректов
    При наличии внутреннего перенаправления на /index.php происходит повторная проверка блоков, в которой срабатывает общий блок для всех остальных запросов, что и приводит к нежелательному редиректу. Чтобы этого избежать, мы должны сделать /index.php внутренним, используя директиву internal.

Обновленная конфигурация Nginx

Ниже представлена обновленная версия конфигурации Nginx:

upstream php {
    server unix:/var/run/php/php7.4-fpm.sock;
}

server {
    listen 80;
    listen [::]:80;

    server_name www.example.com;

    root /var/www/www.example.com;
    index index.php;

    # Обработка запросов к API
    location ~ ^/api(?:/(.*))?$ {
        # Внутренняя обработка запроса
        try_files $uri $uri/ /index.php$is_args$args;
    }

    # Общий редирект для всех остальных запросов
    location / {
        return 301 https://www.other-example.com/foo.html;
    }

    # Внутренняя обработка index.php
    location = /index.php {
        internal;  # Запрет на доступ к этому пути извне
        try_files $fastcgi_script_name =404;

        # Регулярное выражение для разделения $uri на $fastcgi_script_name и $fastcgi_path
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;

        # Присвоение PATH_INFO
        set $path_info $fastcgi_path_info;
        fastcgi_param PATH_INFO $path_info;

        fastcgi_index index.php;
        include fastcgi.conf;
        include fastcgi_params;

        fastcgi_pass php;
    }
}

Подробное объяснение конфигурации

  • Блок location /api: этот блок гарантирует, что все запросы к пути /api будут обрабатываться. Если приходит запрос, который не соответствует существующим файлам или папкам, он будет перенаправлен на index.php, но это происходит внутри Nginx (отметка internal для index.php не позволит внешним запросам к этому пути).

  • Блок location /: для всех остальных запросов, которые не попадают под предыдущий блок, выполняется редирект на указанный внешний адрес с кодом 301 (постоянный редирект).

  • Внутренний блок для index.php: таким образом, доступ к index.php возможен только из внутреннего процесса Nginx, что предотвращает ненужный редирект при обращении к API.

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...