Вопрос или проблема
Это возможно не сработает, но всё же попробую: я хочу создать образ контейнера для Apache без необходимости работать от имени root. Я настроил VirtualHost на прослушивание порта 8080 вместо любого порта <1024. Перед Apache стоит прокси, который перенаправляет трафик с порта 80 на порт 8080 контейнера. Однако, когда я заставляю Apache делать редирект, он всегда добавляет порт в заголовок Location. Поэтому возвращаемый редирект выглядит как "Location: http://mysite:8080/index“, что не сработает, так как на порту 8080 нет публичного слушателя.
Есть ли способ сказать Apache никогда не добавлять номер порта к этим редиректам? Я пытался поиграть с UseCanonicalPhysicalPort, но это не дало желаемого эффекта. Есть ли способ предотвратить добавление номера порта к чему угодно?
ИЗМЕНЕНИЕ
Хорошо, похоже, это поведение закодировано в программе, когда я смотрю на исходный код mod_rewrite, я замечаю, что оно вызывает “ap_is_default_port”, чтобы определить, нужно ли добавлять порт в ответ или нет. Единственный способ переопределить это — переопределить эту строку в исходном коде, я думаю. Если кто-то знает лучший ответ, я все уши.
Слишком длинно для комментария
Имхо, вы пытаетесь решить не ту проблему
Проблема не в том, что Apache включает номер порта, на котором он слушает, в заголовок Location (потому что для Apache это правильное поведение, он действительно слушает на порту 8080), а проблема в том, что ваш обратный прокси это не исправляет.
Обычно обратный прокси должен переписывать заголовки расположения (и другие) и изменять любые ссылки на внутренние URI, которые используют серверы на заднем плане, на URI, которые клиенты должны использовать, чтобы достичь обратного прокси / балансировщика нагрузки.
Проверьте конфигурацию вашего обратного прокси (traefik), чтобы выяснить, почему этого не происходит.
Суть в том, что это можно достичь, отправив заголовок Host: mysite:80
в Apache, изменив конфигурацию обратного прокси. Обратите внимание, что часть :80
имеет значение. (К сожалению, этот ответ лежит вне конфигурации Apache, но поскольку редакция опроса спрашивает о любом лучшем ответе, это определенно лучше, чем модификация исходного кода Apache и его перекомпиляция.)
Пример фрагмента конфигурации Traefik:
labels:
- "traefik.http.middlewares.testHeader.headers.customrequestheaders.Host=mysite:80"
Для nginx (который я тестировал более обширно):
proxy_set_header Host mysite:80;
# Или, чтобы использовать имя хоста из запроса:
proxy_set_header Host $host:80;
# Обратите внимание, что переменная $host никогда не содержит номера порта, даже если он присутствует в заголовке Host оригинального запроса.
Для HTTPS используйте 443
вместо этого. В любом случае, явный номер порта, равный порту по умолчанию для протокола, нужен в заголовке запроса, чтобы заставить Apache сгенерировать заголовок ответа без номера порта, например Location: http://mysite/index"
.
Как это работает:
Оказывается, Apache HTTPd генерирует URL-адреса перенаправления, смотря на заголовок запроса Host
, а также на номер порта, на котором он слушает. Изучая исходный код (ссылка на него была в вопросе), эксперименты показывают, что Apache использует следующую последовательность предпочтений для того, чтобы сделать URL абсолютным (например, при генерации перенаправления Location
):
- Явленный номер порта из
Host
, как вexample.com:1111
- Порт, на котором слушает Apache (где был получен запрос), как в
Listen 2222
- Порт по умолчанию протокола (443 для HTTPS, 80 для HTTP)
Интересно, что Host: example.com
обрабатывается иначе, чем Host: example.com:80
на первом этапе. Я не специалист по IETF RFC, но ожидал бы, что первый является сокращением второго на основе правил нормализации URL, но, похоже, Apache интерпретировал это иначе и считает порт неопределенным в первом случае. Поэтому добавление номера порта к Host
переопределяет порт прослушивания.
Перед генерацией заголовка Location
Apache нормализует сгенерированный абсолютный URL. В этом процессе он осознает, что :80
является стандартным портом HTTP и исключает его из заголовка ответа, то же самое для https://myhost:443
. Не углубляясь в детали исходного кода, URL прошел через цепочку преобразований вроде этого:
/index ==> http://mysite:80/index ==> http://mysite/index
…что является долгим и окольным путем к достижению желаемого результата. Я знаю, что это старый вопрос, но надеюсь, что это поможет следующему человеку, который сюда попал через поиск.
Ответ или решение
Перенаправление в Apache без указания номера порта: Полное решение вопроса
В данной статье мы разберем, как корректно настроить перенаправление в Apache без добавления номера порта в заголовок Location
, особенно когда Apache работает на нестандартном порту (например, 8080) и перед ним расположен обратный прокси-сервер, который направляет трафик с порта 80.
Ситуация
Вы хотите создать контейнерный образ для Apache, который не требует запуска с привилегиями суперпользователя. Для этого вы настроили VirtualHost
, чтобы он прослушивал порт 8080. Ваш обратный прокси (например, Traefik или Nginx) направляет трафик с порта 80 на порт 8080 внутри контейнера. Однако, когда Apache выполняет перенаправление, он добавляет номер порта в заголовок Location
, что приводит к ошибке, так как клиент пытается обратиться к порту 8080, к которому нет доступа извне.
Причина проблемы
Apache добавляет номер порта в заголовок Location
, основываясь на информации из заголовка Host
, а также на порту, к которому он подключен. Стандартное поведение Apache подразумевает, что если запрос приходит на определенный порт, этот порт будет включен в ответ. Это поведение можно изменить, корректируя заголовок Host
, который передается к серверу Apache.
Решение проблемы
Чтобы избежать добавления номера порта в заголовок Location
, необходимо внести изменения в конфигурацию вашего обратного прокси, чтобы он отправлял правильный заголовок Host
.
Для Nginx
Если вы используете Nginx в качестве обратного прокси, добавьте следующее в ваш конфигурационный файл:
proxy_set_header Host mysite:80;
Или, чтобы использовать хост, указанный в запросе:
proxy_set_header Host $host:80;
Для Traefik
Если вы используете Traefik, добавьте следующую метку в вашу конфигурацию:
labels:
- "traefik.http.middlewares.testHeader.headers.customrequestheaders.Host=mysite:80"
Как это работает
Apache генерирует URL для перенаправления, анализируя заголовок Host
и порт, на котором он слушает. Если заголовок Host
содержит явный номер порта (например, mysite:80
), то этот номер будет использоваться при формировании заголовка Location
. Если такой номер отсутствует, Apache использует свой порт, который находится в конфигурации.
Последовательность, по которой Apache генерирует абсолютный URL, выглядит следующим образом:
- Явный номер порта из
Host
, например,example.com:1111
. - Порт, на котором Apache принимает запрос, например,
Listen 8080
. - Стандартный порт для протокола (443 для HTTPS и 80 для HTTP).
Таким образом, если вы измените заголовок Host
на mysite:80
, Apache может правильно генерировать URL без добавления порта в заголовок Location
.
Заключение
Корректируя заголовок Host
, вы можете избежать добавления порта, на котором Apache слушает, в перенаправлениях. Это решение позволит вашему приложению правильно обрабатывать запросы без необходимости изменения исходного кода Apache, что является предпочтительным вариантом для поддержания безопасной и стабильной работы вашего сервера.
Если у вас остались вопросы или вам нужна помощь с конкретной конфигурацией, не стесняйтесь обращаться за дополнительными разъяснениями.