nginx proxy pass php fpm
PHP FastCGI Example
This example is for newer PHP (>= 5.3.3) using the included PHP FPM (FastCGI Process Manager).
This guide assume PHP FPM already installed and configured either using tcp port ( 127.0.0.1:9000 ) or unix socket ( /var/run/php-fpm.sock ).
There are many guide about configuring NGINX with PHP FPM, but many of them are incomplete (don’t handle PATH_INFO correctly) or contain security issues (don’t check whether the script is indeed php file).
FastCGI ParamsВ¶
First thing, I recommend keeping all your typical FCGI settings in a single file and importing them.
For example on debian and ubuntu by default there is /etc/nginx/fastcgi_params file that should look like this:
Please note if you’re using Ubuntu Precise (12.04), I change SCRIPT_FILENAME and add PATH_INFO params.
Connecting NGINX to PHP FPMВ¶
Now we must tell NGINX to proxy requests to PHP FPM via the FCGI protocol:
If you’re using unix socket change fastcgi_pass to:
TestingВ¶
Create test.php on NGINX document root containing just:
In the browser try to request: # /test.php # /test.php/ # /test.php/foo # /test.php/foo/bar.php # /test.php/foo/bar.php?v=1
Pay attention to the value of REQUEST_URI, SCRIPT_FILENAME, SCRIPT_NAME, PATH_INFO and PHP_SELF.
NotesВ¶
The if lets NGINX check whether the *.php does indeed exist to prevent NGINX to feeding PHP FPM non php script file (like uploaded image).
The SCRIPT_FILENAME parameter is required as it is passed to PHP FPM to determine the script name.
If you see a blank page in browser, please check if SCRIPT_FILENAME parameter is set.
How do I get PHP-FPM to work with nginx-proxy with FastCGI?
I have followed the instructions for nginx-proxy to the best of my ability and have boiled it down to a very simple way to re-create the issue. Here’s my docker-compose.yml file:
I then drop in a simple index.php file by running:
The logs for the web_proxy_1 container don’t indicate anything out of the ordinary, as far as I know:
The logs for the web_fpm_1 container show that nothing gets sent except a 200 response:
What am I doing wrong?
Incidentally, I have asked this question on the nginx-proxy repo, the nginx-proxy Google Group, and the php repo. I either get no response or they pass the buck.
2 Answers 2
The default generated config of nginx-proxy is not fully working.
I think something is messed up with VIRTUAL_ROOT environment variable, because the root of the problem is PHP getting a wrong path via SCRIPT_FILENAME (that’s why you see no PHP output) and there is no try_files with =404 symbol (that’s why you get 200 with everything).
I have a prepared working setup using docker-compose in GitHub to demonstrate that it would work with an existing SCRIPT_FILENAME in the nginx config.
Another, yet different approach would be to pack PHP and a manually configured webserver (nginx) in a project and having the automated reverse nginx proxy in a standalone project. This would cost you an additional process running but gives you more control and easier deployment.
Настройка прокси Nginx
В этой статье мы рассмотрим как выполняется настройка прокси Nginx, а также поговорим как заставить эту связку правильно обрабатывать HTTPS запросы и передадим управление статическими файлами Nginx.
Как это будет работать?
Допустим, у нас есть несколько доменов example.com, sample.org, test.io. Первые два будут обрабатываться Apache, последний только Nginx. Все запросы будут поступать к Nginx, который работает на порту 80, если это запрос к одному из доменов Apache и он требует работы PHP, тогда он будет передан веб-серверу Apache, который работает на порту 8080.
Если же это запрос статического файла, то мы будем обрабатывать его тут же с помощью Nginx для увеличения производительности. Что касается поддержки SSL, то мы собираемся использовать модуль mod_pref чтобы заменить все необходимые заголовки для нормальной работы связки. Начнем с настройки Apache.
Настройка Apache для работы прокси
Мы не будем подробно рассматривать как настроить Apache в вашей системе, все это уже описано в статье настройка Apache, сегодня же мы остановимся на настройках, необходимых для работы прокси.
Мы будем использовать Apache с интерпретатором PHP, установленным в виде модуля php-fpm. Это обеспечит лучшую общую производительность системы. Сначала установим все нужные пакеты:
sudo apt install apache2 libapache2-mod-fastcgi php-fpm
Поскольку нам нужно, чтобы Apache работал на порту 8080 нужно изменить конфигурационные файлы веб-сервера:
sudo vi /etc/apache2/ports.conf
Замените значение строки Listen с 80 на 8080, затем сохраните изменения в файле. Далее изменим порт для веб-сайта по умолчанию:
sudo vi /etc/apache2/sites-available/000-default.conf
Точно так же замените значение порта с 80 на 8080. Затем сохраните изменения и перезапустите веб-сервер:
sudo systemctl reload apache2
Теперь вы можете проверить на каком порту будет ожидать соединений Apache, если все было сделано правильно, то это будет 8080:
Чтобы показать настройку прокси Nginx более наглядно, создадим один виртуальный хост (домен). Сначала создадим каталог для нашего хоста:
sudo mkdir /var/www/test.com/
Затем файлы index.html и phpinfo.php:
Test com
Затем настроим файлы конфигурации виртуальных хостов для каждого из доменов:
sudo vi /etc/apache2/sites-available/test.com.conf
ServerName test.com
ServerAlias www.test.com
DocumentRoot /var/www/test.com
AllowOverride All
Обратите внимание на порт, тут тоже нужно указать 8080. Для тестирования работы php нам понадобится скрипт с вызовом функции:
echo » » | sudo tee /var/www/test.com/phpinfo.php
Осталось включить конфигурацию для только что созданного сайта и перезапустить веб-сервер:
sudo a2ensite test.com
sudo systemctl reload apache2
Настройка Apache для php-fpm
По умолчанию в Apache используется модуль mod-php для выполнения php скриптов. Сначала необходимо его отключить:
sudo a2dismod php7.0
Затем мы настроим работу mod_fastcgi с помощью модуля mod_actions, для этого нужно его активировать:
sudo a2enmod actions
Затем создадим конфигурационный файл fastcgi.conf:
sudo vi /etc/apache2/mods-available/fastcgi.conf
Сохраните изменения, активируйте модуль и проверьте конфигурацию веб-сервера:
sudo a2enmod fastcgi
Вы увидите сообщение, что с синтаксисом конфигурационных файлов все хорошо. Если программа выдаст сообщение Could not reliably determine the server’s fully qualified domain name, using 127.0.1.1, его можно игнорировать. Далее перезапустите Apache:
sudo systemctl restart apache2
Проверка работы Apache
Добавьте свои домены в файл hosts, если они не зарегистрированы и доступны только с локальной машины:
Затем откройте сайт в браузере, чтобы убедится, в том что все работает:
Настройка прокси Nginx
Теперь, когда Apache полностью готов к работе в качестве веб-сервера, перейдем к настройке прокси сервера Nginx, мы можем заняться настройкой самого Nginx. Как я уже сказал, мы будем перенаправлять все динамические запросы к Apache, чтобы пользователь смог получить поддержку файлов htaccess и другие преимущества, а статические файлы будем обрабатывать в Nginx.
Сначала установите Nginx, если вы этого еще не сделали:
sudo apt install nginx
Дальше создадим виртуальный хост Nginx с несколькими доменами, с помощью которого и будет выполняться проксирование Nginx:
sudo vi /etc/nginx/sites-available/apache
Для использования nginx в качестве прокси мы передаем в команде proxy_pass адрес и порт веб-сервера, а в заголовках передаем те значения, которые будут нужны Apache для правильного формирования документа. Сохраните файл и активируйте его:
Затем проверьте конфигурацию и перезапустите Nginx:
sudo systemctl reload nginx
Теперь вы можете проверить работу вашего сайта в браузере, если вы откроете скрипт phpinfo, то увидите, что он был обработан с помощью Apache, но возвращен Nginx.
Как вы могли убедится, теперь применяется nginx в качестве прокси. Теперь можно закрыть прямой доступ к Apache из сети с помощью iptables:
Настройка правильной работы SSL
Дальше рассмотрим как выполняется настройка https прокси Nginx. Как я уже сказал, для правильной работы SSL нам понадобится модуль Apache mod_rpaf. Он устанавливает заголовки и переменные таким образом, чтобы прокси мог без проблем использовать https. Его можно установить из официальных репозиториев:
sudo apt install libapache2-mod-rpaf
Затем создайте конфигурационный файл для этого модуля:
sudo vi /etc/apache2/mods-available/rpaf.conf
RPAFEnable On
RPAFHeader X-Real-Ip
RPAFProxyIPs ваш_внешний_ip_адрес
RPAFSetHostName On
Строка RPAFProxyIPs задает IP адрес вашего прокси. После завершения настройки активируйте модуль:
Осталось перезапустить Apache:
sudo systemctl reload apache2
Дальше нам нужно создать наши сертификаты с помощью OpenSSL:
sudo mkdir /etc/nginx/ssl/
А файл виртуальных хостов с поддержкой SSL теперь будет выглядеть во так:
sudo nano /etc/nginx/sites-available/apache
Сохраните файл конфигурации и перезапустите Nginx:
Теперь вы можете открыть наш домен в браузере по HTTS и убедится что прокси отлично работает. Обратите внимание, что если вы используете самоподписанный сертификат, то вам придется добавить его в исключения браузера:
Поддержка защищенного протокола включена и SERVER_PORT имеет значение 443, все работает прозрачно, как бы проксирование nginx не осуществляется, а мы направляем запросы непосредственно к Apache.
Статические файлы через Nginx
Чтобы уменьшить нагрузку на Apache мы можем обрабатывать все статические файлы в Nginx, как правило, это очень сильно увеличивает выдерживаемую нагрузку, поскольку Nginx способен работать быстрее с большим количеством подключений и занимать меньше ресурсов.
Нам нужно добавить несколько строк в /etc/nginx/sites-available/apache:
Теперь откройте несколько раз сайт в браузере, динамические страницы phpinfo.php и статическую index.html, а затем посмотрите в лог файл, где вы обнаружите что Apache обрабатывает только динамику:
Теперь проксирование Nginx работает так же как и на большинстве серверов интернета.
Выводы
В этой статье мы рассмотрели как выполняется настройка прокси Nginx, а точнее, как использовать nginx как прокси для Apache. Для новичков эти настройки могут показаться сложными, но если разобраться, то все обязательно получится. Если у вас остались вопросы, спрашивайте в комментариях.
Использование Nginx FastCGI Cache
FastCGI Cache — это система кэширования данных реализованая на уровне HTTP-сервера Nginx.
Преимущество FastCGI Cache заключается в том, что Nginx вернёт закешированный ответ пользователю сразу, как только получит запрос, при этом слой приложения не будет вовсе обрабатывать поступивший HTTP-запрос, если он имеется в кэше Nginx.
Использование FastCGI Cache — отличный способ снизить нагрузку на вашу систему.
Если на вашем сайте есть страницы, которые изменяются редко или задержка обновления информации на некоторое время не критична, то FastCGI Cache именно то, что нужно.
Схема работы Nginx FastCGI Cache
Если на сервер Nginx пришёл HTTP-запрос и некоторое время назад ответ на такой же запрос был помещён в кэш, то Nginx не станет передавать данный запрос на выполнение PHP-FPM, в место этого Nginx вернёт результат из кэша.
Задача
Предположим у нас есть web-система управления полётом на луну, которая написана на PHP. Каждый пользователь должен ввести свой логин и пароль, чтобы войти в систему и оказаться на главной странице космического приложения.
Главная страница нашего ресурса, на которую попадают пользователи прошедшие этап аутентификации, очень популярная. Все пользователи ежедневно многократно просматривают эту страницу. На ней выводится большое количество всевозможных данных. Чтобы сгенерировать эту страницу требуется выполнить порядка тридцати SQL-запросов в различные базы данных.
Важно отметить, у пользователей данные, которые они видят на главной странице могут отличаться.
Нам известно, что информация, которая выводится на этой странице может обновляться с задержкой в один час. Данные не потеряют свою ценность даже есть они немного устареют.
Что из этого следует?
У каждого пользователя, прошедшего аутентификацию должна быть своя версия кэша главной страницы.
До ввода логина и пароля и после этого главная страница системы выглядит по-разному. Не прошедшие аутентификацию пользователи видят только форму для входа в систему.
Мы можем хранить данные в кэше в течение 1 часа.
Решение
Изначально наш виртуальных хост сконфигурирован следующим образом:
Первое, что мы сделаем добавим директиву fastcgi_cache_path в в контексте http.
Первый аргумент /tmp/nginx_cache определяет место на сервере, где будет сохранён кэш. Папка /tmp нам подходит, так как она очищается автоматически при перезапуске сервера. Важно отметить, что всё, что будет хранится в /tmp/nginx_cache так же будет находится в оперативной памяти.
Второй аргумент — это уровень подпапок. Мы указали levels=1:2. Это означает, что уровень вложенности будет равняться 2.Нас это устраивает, так как в одной папке у нас не будет большого количества файлов, а значит и замедление доступа к файлам нам не грозит.
Третий аргумент — имя зоны разделяемой памяти кэша. Запись keys_zone=fastcgicache:10m означает, что названием зоны является fastcgicache, а 10m — это размер зоны в мегабайтах. Зона размеров в 10 Мб может хранить данные для примерно 80 000 ключей. Конечно, название зоны может быть другим.
Четвёртый аргумент — inactive=70m, определяет интервал времени, после истечении которого данные автоматически удаляются, в случае, если они не используются. Другими словами, если к данным кэша не обращаются в течение времени, заданного параметром inactive, то данные удаляются, независимо от их свежести. По умолчанию inactive равен 10 минутам.
Пятый аргумент max_size=512m — устанавливает верхний предел размера кэша. По умолчанию используется всё дисковое пространство. При достижении лимита, Nginx удалит наименее востребованные данные.
Далее мы должны задать ключ для кэширования данных. Это можно сделать при помощи директивы fastcgi_cache_key. Данная директива может быть указана в контекстах http, server и location.
Укажим в контексте server директиву fastcgi_cache_key:
Здесь мы указали несколько переменных:
— $scheme
— $request_method
— $host
— $request_uri
— $cookie_codeAuth
Давайте разберёмся с ними подробнее. Нам важно понимать, какие значения будут принимать эти переменные.
Рассмотрим пример, допустим залогиненный пользователь указал в браузере такой запрос:
После выполнения этого запроса Nginx присвоит следующие значения нашим переменным:
Проксирование запросов в nginx с помощью proxy_pass
Хочу рассказать об одной полезной возможности nginx, которую регулярно использую в своих делах. Речь пойдет о настройке проксирования запросов на удаленный сервер с помощью nginx и директивы proxy_pass. Я приведу примеры различных настроек и расскажу, где сам использую данный модуль популярного веб сервера.
Введение
Расскажу на конкретных примерах, где я это использую. Для наглядности и простоты буду прям по порядку перечислять эти варианты:
Надеюсь в общем и целом понятно, о чем идет речь. Вариантов использования много. Я привел самые распространенные, которые пришли в голову и которые использую сам. Из плюсов, которые считаю наиболее полезными именно из своих кейсов, отмечу 2:
С теорией закончил. Перейдем теперь к примерам настройки. В своих примерах я буду использовать следующие обозначения:
blog.zeroxzed.ru | доменное имя тестового сайта |
nginx_srv | имя внешнего сервера с установленным nginx |
blog_srv | локальный сервер с сайтом, куда проксируем соединения |
94.142.141.246 | внешний ip nginx_srv |
192.168.13.31 | ip адрес blog_srv |
77.37.224.139 | ip адрес клиента, с которого я буду заходить на сайт |
Настройка proxy_pass в nginx
Заходим по адресу http://blog.zeroxzed.ru. Мы должны попасть на blog_srv, где тоже должен работать какой-то веб сервер. В моем случае это будет тоже nginx. У вас должно открыться содержимое, аналогичное тому, что вы увидите, набрав http://192.168.13.31 в локальной сети. Если что-то не работает, то проверьте сначала, что по адресу директивы proxy_pass у вас все корректно работает.
Посмотрим логи на обоих сервера. На nginx_srv вижу свой запрос:
Как мы видим, запрос сначала пришел на nginx_srv, был переправлен на blog_srv, куда он пришел уже с адресом отправителя 94.142.141.246. Это адрес nginx_srv. Реальный же ip адрес клиента мы видим только в самом конце лога. Это неудобно, так как директива php REMOTE_ADDR не будет возвращать настоящий ip адрес клиента. А он очень часто бывает нужен. Мы это дальше исправим, а пока создадим в корне сайта на chat_srv тестовую страничку для проверки ip адреса клиента следующего содержания:
Назовем ее myip.php. Перейдем по адресу http://blog.zeroxzed.ru/myip.php и проверим, как сервер определит наш адрес. Никак не определит 🙂 Он покажет адрес nginx_srv. Исправляем это и учим nginx передавать реальный ip адрес клиента на сервер.
Передача реального ip (real ip) адреса клиента в nginx при proxy_pass
Полностью секция server на blog_srv в самом простом варианте получается следующей:
Сохраняем конфиг, перечитываем его и снова проверяем http://blog.zeroxzed.ru/myip.php. Вы должны увидеть свой реальный ip адрес. Его же вы увидите в логе web сервера на blog_srv.
Дальше рассмотрим более сложные конфигурации.
Передача https через nginx с помощью proxy pass
Если у вас сайт работает по https, то достаточно настроить ssl только на nginx_srv, если вы не беспокоитесь за передачу информации от nginx_srv к blog_srv. Она может осуществляться по незащищенному протоколу. Рассмотрю пример с бесплатным сертификатом let’s encrypt. Это как раз один из кейсов, когда я использую proxy_pass. Очень удобно настроить на одном сервере автоматическое получение всех необходимых сертификатов. Подробно настройку let’s encrypt я рассматривал отдельно. Сейчас будем считать, что у вас стоит certbot и все готово для нового сертификата, который потом будет автоматически обновляться.
Перечитывайте конфиг nginx и получайте сертификат. После этого конфиг меняется на следующий:
Проверяем, что получилось.
Наш сайт работает по https, при том, что мы вообще не трогали сервер, где этот сайт располагается. Конкретно с web сайтом это может быть не так актуально, но если вы проксируете запросы не на обычный сайт, а на какой-то нестандартный сервис, который трудно перевести на https, это может быть хорошим решением.
Проксирование определенной директории или файлов
Рассмотрим еще один пример. Допустим, у вас форум живет в директории http://blog.zeroxzed.ru/forum/, вы хотите вынести форум на отдельный web сервер для увеличения быстродействия. Для этого к предыдущему конфигу добавьте еще один location.
Еще одно популярное решение. Вы можете отдавать картинки с одного сервера, а все остальное с другого. В моем примере, картинки будут жить на том же сервере, где nginx, а остальной сайт на другом сервере. Тогда у нас должна быть примерно такая конфигурация локаций.
Чтобы все это работало корректно, необходимо, чтобы сам сайт умел правильно размещать свои изображения. Вариантов это организовать множество. Можно как на сервере монтировать сетевые паки различными способами, так и программистам изменять код для управления размещением изображений. В любом случае, это комплексный подход к работе с сайтом.
Существует очень много директив для управления прокси-соединениями. Все они описаны в соответствующей документации nginx. Я не большой специалист по настройке nginx. В основном использую свои готовые конфиги, зачастую даже не вникая в суть, если получается сразу решить задачу. Подсматриваю что-то у других, записываю к себе, стараюсь разобраться.
Особое внимание следует уделить директивам кэширования proxy_cache, если в этом есть потребность. Можно существенно увеличить отклик веб сайта, если подходящим образом настроить отдачу кэша. Но это тонкий момент и нужно настраивать в каждом конкретном случае отдельно. Готовых рецептов тут не бывает.
Более подробно о комплексной настройке nginx читайте в отдельной большой статье с моими личными примерами.
Часто задаваемые вопросы по теме статьи (FAQ)
В общем случае не обязательно. Но есть некоторый софт, который не может корректно работать в таком режиме. Он не понимает, как корректно обрабатывать такую ситуацию. Может создавать ссылки вида http://site.ru:443, которые будут являться ошибочными. В таком случае необходимо настроить обмен между nginx и бэк сервером тоже https соединение.
В такой ситуации может помочь правка файла /etc/hosts на самом бэкенде. Сделайте там статическую запись с именем сайта и локальным ip адресом. Тогда запросы с самого сервера будут приходить на него же локально, а не на nginx proxy.
Однозначного ответа на этот вопрос не может быть. В чем-то это схожий софт, но есть и существенные отличия. В общем случае, описанном в статье, принципиальной разницы нет. У haproxy в бесплатной версии есть функционал, который присутсвует только в nginx plus. Так что нужно смотреть по конкретным задачам и решать, какой продукт подойдет лучше.
Конечно да. Но на практике они очень малы, если nginx и целевой сервер находятся в общей локальной сети. С учетом задержек при передачи пакетов по сети интернет, этими локальными задержками можно пренебречь. Они будут ничтожно малы. Если же вы проксируете запросы через интернет, то нужно внимательно смотреть величину задержек между nginx и бэкендом. Желательно их делать минимальными, располагая сервера поближе друг к другу.
Заключение
На этом у меня все. Не рассмотрел еще один возможный вариант, когда вы проксируете https сайт и передаете инфу на бэкенд тоже по https. Нет под рукой готового примера, чтобы проверить, а заочно не стал рисовать конфиг. По идее, ничего сложного в этом нет, настраиваете nginx на обоих серверах с одним и тем же сертификатом. Но наверняка не скажу, что все заработает при этом. Возможно, есть какие-то нюансы с таким проксированием. Мне обычно не приходится так делать.
Как я уже писал в начале, в основном проксирую запросы с одного внешнего веб сервера на закрытый периметр сети, куда нет никому доступа. В этом случае у меня нет необходимости использовать https при передаче запросов на бэкенд. В качестве бэкенда не обязательно будет отдельный сервер. Это запросто может быть контейнер на этом же сервере.
Онлайн курс по Kubernetes
Онлайн-курс по Kubernetes – для разработчиков, администраторов, технических лидеров, которые хотят изучить современную платформу для микросервисов Kubernetes. Самый полный русскоязычный курс по очень востребованным и хорошо оплачиваемым навыкам. Курс не для новичков – нужно пройти вступительный тест.