Вопрос или проблема
У меня есть работающий сайт (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 осуществляется следующим образом в вашем случае:
- Запрос к
/api/test_endpoint
воспринимается блокомlocation ~ ^/api(?:/(.*))?$
. - В
try_files
он соответствует части/index.php$is_args$args
. Поскольку это последняя часть, это вызывает внутреннее перенаправление. - 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
-
Обработка запросов к /api
Для того чтобы запросы к пути/api
не перенаправлялись на внешний сайт, добавим отдельный блокlocation
для обработки этих запросов. Ваша конфигурация уже содержит это, но в ней необходимо учесть несколько моментов. -
Избежание внутренних редиректов
При наличии внутреннего перенаправления на/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
. Важно протестировать конфигурацию после примененных изменений и убедиться, что запросы обрабатываются согласно ожиданиям.