Настройка nginx и PHP/FastCGI в Windows
Как настроить связку nginx+PHP в Windows и не иметь головной боли.
Принято считать, что замечательный Web-сервер nginx работает только под Unix-подобными операционными системами. Верно, но лишь отчасти. nginx превосходно собирается и в Cygwin. Возникает вопрос: а зачем это надо, собирать nginx под Windows? Ответ: я знаю достаточно много web-разработчиков, работающих в силу тех или иных причин под Windows. И в работе встречаются ситуации, когда на рабочей машине нужно создать конфигурацию, максимально похожую на конфигурацию сервера (production или development в данном случае не важно).
Начну с примера: есть сайт, который позволяет зарегистрированным пользователям скачивать что-либо (музыку, программы, фильмы — не суть важно). Одним из самых распространённых решений в этом случае является использование нескольких серверов. Во многих конфигурациях, которые я видел, "web-мордой" занимается Apache, а на обслуживании закачек стоит nginx+PHP+FastCGI (PHP занимается вопросами авторизации, ведения статистики и т.п.). Сразу оговорюсь, что есть альтернативное решение, когда nginx используется в качестве reverse proxy перед Apache — nginx проксирует запросы к PHP-скриптам Apache, а PHP-скрипт, если ему надо отдать что-то статическое, делает X-Accel-Redirect. Подробнее описано в статьях "Использование X-Accel-Redirect с Nginx для реализации контролируемых скачиваний" и "Использование Nginx Как Reverse-Proxy Сервера На Загруженных Сайтах".
Отвлекусь от темы и скажу пару слов про альтернативную конфигурацию: она работает на "ура", если сайт отдаёт в основном статический контент. Если же большая часть запросов идёт к PHP, то логичнее поставить nginx за Apache (в противном случае будет тратиться время на проксирование запроса). Еще один минус у такой конфигурации — использование менеджеров закачек (всякие там ReGet, FlashGet) "ложит" Apache. Можно, конечно, ввести ограничения на одновременное количество заказчек, а можно настроить PHP работать в паре с nginx.
Возвращаясь к теме разговора: а теперь предположим, что это всё нужно реализовать локально (для тестирования). Вот в таких ситуациях nginx для Windows очень хорошо помогает
В Cygwin сборка nginx проходит практически без проблем (хотя более старые версии приходилось немного патчить руками), на выходе получаем nginx.exe
Я не буду вдаваться в детали конфигурирования nginx, потому что есть замечательная статья "Настройка Nginx для поддержки PHP при помощи FastCGI", просто отмечу некоторые детали, упрощающие жизнь.
Итак, у нас есть PHP, Cygwin и nginx. К сожалению, nginx не поддерживает PHP как модуль (подобно Apache), поэтому nginx и PHP могут работать только по протоколу FastCGI (возможно, и по обычному CGI, я не пробовал — в любом случае, производительность при работе через CGI будет значительно ниже).
Для определённости будем полагать, что nginx у нас сконфигурирован следующим образом (я привожу только часть, относящуюся к FastCGI):
location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME disk:/path/to$fastcgi_script_name; include conf/fastcgi_params; }
Ключевой момент: в fastcgi_param
должен быть путь, который поймёт интерпретатор PHP. Если интерпретатор собран в Windows, то путь тоже должен быть в формате Windows (например, c:/web/mysite.com/scripts). Хвостового слэша быть не должно, потому что $fastcgi_script_name
именно с него и начинается. То есть если пользователь запросил http://mysite.com/test.php, то $fastcgi_script_name
будет равен /test.php
.
Файл conf/fastcgi_params:
fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name; # PHP only, required if PHP was built with --enable-force-cgi-redirect fastcgi_param REDIRECT_STATUS 200;
Для удобства, установим nginx как сервис Windows. Для этого можно использовать, например, cygrunsrv:
Ключевым здесь является параметр --neverexits
— без него Windows будет жаловаться, что процесс был остановлен (из-за того, что nginx fork()
'ается). Хотя это неправильное использование --neverexits
.
Переходим к настройке PHP. По счастью, в Windows-версии PHP уже есть модуль с поддержкой FastCGI (он называется php-cgi.exe), так что пересобирать PHP не придётся (сборка PHP под Cygwin — неблагодарное занятие из-за парочки глюков в libtool; если у меня будет желание, как нибудь напишу). В статье приводится скрипт для запуска PHP/FastCGI, но мы поступим проще.
Сначало об одной не очень документированной особенности PHP/FastCGI: он имеет тенденцию "незаметно умирать" после некоторого числа запросов. Это связано со значением переменной окружения PHP_FCGI_MAX_REQUESTS
, определяющей, какое максимальное количество запросов может "пережить" PHP. Можно, кончено, поставить "космическое" значение, но 2 миллиарда запросов — так ли это много? На отрицательные значения переменной PHP громко ругается. Экспериментально было выяснено, что 0 — это то, что нужно (в документации я это не нашел).
Запускаем PHP:
php-cgi -b 127.0.0.1:9000 -c path/to/php.ini
А теперь как установить PHP/FastCGI сервисом Windows:
Строка "-b 127.0.0.1:9000 -c path/to/php.ini
" будет передана интерпретатору в качестве командной строки, поэтому путь path/to/php.ini
должен быть путем Windows, если интепретатор собран под Windows.
Собственно, всё
Замечательно!
Великолепно!
Это именно то, что я искал так долго, и собирал по крупицам! А вы все собрали вместе, и уложили один за другим - спасибо вам преогроменное!
С уважением, babr
babr, очень рад, что Вам это пригодилось!
Уважаемый автор, у меня к вам вопрос.
Я пользуюсь вот этой сборкой nginx под Windows: http://www.kevinworthington.com/nginx/win32/ очевидно автор ее так же компилил из под cygwin.
Т.к. я не имею достаточного опыта работы с cygwin, а мой опыт работы с Unix-системами находится на уровне пользователя графической оболочки KDE, то я решился спросить у вас.
Ситуация в следующем:
Когда я в настройках nginx пытаюсь прописать такое:
server {
…
root C:/Apache/htodcs/tos/trunk/src/www;
…
}
То получаю ошибку nginx (в логе то есть):
2008/07/19 16:08:40 [error] 3884#0: *1 open() “/cygdrive/c/nginx/C:/Apache/htodcs/tos/trunk/src/www/favicon.ico” failed (2: No such file or directory), client: 127.0.0.13, server: local.timeout, request: “GET /favicon.ico HTTP/1.1″, host: “local.timeout”
2008/07/19 16:08:40 [error] 3884#0: *1 open() “/cygdrive/c/nginx/C:/Apache/htodcs/tos/trunk/src/www/err/404.html” failed (2: No such file or directory), client: 127.0.0.13, server: local.timeout, request: “GET /favicon.ico HTTP/1.1″, host: “local.timeout”
2008/07/19 16:08:43 [error] 3884#0: *1 open() “/cygdrive/c/nginx/C:/Apache/htodcs/tos/trunk/src/www/favicon.ico” failed (2: No such file or directory), client: 127.0.0.13, server: local.timeout, request: “GET /favicon.ico HTTP/1.1″, host: “local.timeout”
2008/07/19 16:08:43 [error] 3884#0: *1 open() “/cygdrive/c/nginx/C:/Apache/htodcs/tos/trunk/src/www/err/404.html” failed (2: No such file or directory), client: 127.0.0.13, server: local.timeout, request: “GET /favicon.ico HTTP/1.1″, host: “local.timeout”
2008/07/19 16:09:32 [error] 3884#0: *2 open() “/cygdrive/c/nginx/C:/Apache/htodcs/tos/trunk/src/www/err/404.html” failed (2: No such file or directory), client: 127.0.0.13, server: local.timeout, request: “GET /index.php HTTP/1.1″, upstream: “fastcgi://127.0.0.1:9000″, host: “local.timeout”
То есть очевидно, nginx пытается танцевать относительно какойго-то начального каталога /cygdrive/ , и это жестко защито. Если я переношу файлы сайта из Apache-каталога htdocs в nginx-каталог html - то все замечательно работает. Но такой вариант меня не устраивает по ряду специфических причин.
Буду вам признателен, если вы хотя бы укажете направление в котором копать.
С уважением, Савунов Василий (babr)
Василий, попробуйте так:
Дело в том, что в Cygwin, как и в Unix, файловая система плоская — нет разделения на диски. Для эмуляции (не знаю, как выразиться точнее) логических дисков используется точка монтирования (каталог) /cygdrive: например, диск C: становится /cygdrive/c, диск D: — /cygdrive/d и т.д. То есть путь
C:\Apache\htdocs\tos\trunk\src\www
с точки зрения Cygwin будет/cygdrive/c/Apache/htdocs/tos/trunk/src/www
.Vladimir - спасибо вам. Слова “эмуляция” и “точка монтирования” мне понятны вполне, так что ваш ответ полностью отвечает на мой запрос.
Спасибо что уделили мне время!
С уважением, Савунов Василий (babr)
Василий, не за что! Надеюсь, что помогло?
Vladimir, спасибо, да, помогло
Давно надо уже на Linux мигрировать, но лень все время берет свое. Уж и работал на Linux (ставили коллеги, не я), и более-менее знаю основные команды Linux, и crontab могу настроить, и конфиг nginx поправить могу, а нет - не могу заставить себя сесть, и убить время за установкой Linux-дистрибутива.
Так что ваша статья как нельзя кстати. Полностью отвечает моим нынешним запросам.
Бредовая ситуевина выходит - одновременно веду разработку работая и под nginx, и под Apache, запуская для разных проектов php то как FastCGI, то как модуль Apache… :))) Но такова специфика работы, и проектов.
Спасибо вам еще раз!
С уважением, Савунов Василий (babr)
У меня просто VMware рухнула, придавив Windows, я счел это знаком и перешел на Linux. И жена уже привыкла.
Так они же могут вместе жить? PHP как модуль Апача, а php-cgi как FastCGI-модуль в nginx.
С VMWare я даже не стал связываться.
“Так они же могут вместе жить? PHP как модуль Апача, а php-cgi как FastCGI-модуль в nginx.”
А они и живут.
Nginx выдает 502 ошибку.
Вот что написанно в error.log-e
2008/08/13 23:04:38 [error] 1836#0: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: localhost, request: “GET /index.php HTTP/1.1″, upstream: “fastcgi://127.0.0.1:9000″, host: “localhost”
2008/08/13 23:04:43 [error] 1836#0: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: localhost, request: “GET /index.php HTTP/1.1″, upstream: “fastcgi://127.0.0.1:9000″, host: “localhost”
2008/08/13 23:05:08 [error] 1836#0: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: localhost, request: “GET /index.php HTTP/1.1″, upstream: “fastcgi://127.0.0.1:9000″, host: “localhost”
Буду вам признателен если подскажите, что нужно исправить
Valodik, а можно на конфиги краем глаза взглянуть?
PS - с очень большой вероятностью у Вас не запущен PHP/CGI, либо же он слушает другой порт. Посмотрите выдачу
netstat -an
Вы правы не слушает на порт 127.0.0.1:9000
Вот результаты netstat -an
А это из nginx.conf
А сам-то php-cgi Вы запустили?
Выполните эти две команды из консоли (cmd.exe):
Вместо
C:\php\php.ini
должен быть путь к Вашемуphp.ini
.Мой php-интерпретатор никак не хочет принимать параметр -b.
Вот инфо про используемых интерпретаторов
PHP 4.4.2 (cgi-fcgi) (built: Jan 13 2006 13:53:43)
Copyright (c) 1997-2006 The PHP Group
Zend Engine v1.3.0, Copyright (c) 1998-2004 Zend Technologies
PHP 5.0.4 (cgi-fcgi) (built: Mar 31 2005 02:45:43)
Copyright (c) 1997-2004 The PHP Group
Zend Engine v2.0.4-dev, Copyright (c) 1998-2004 Zend Technologies
Valodik, запустите Ваш
php-cgi
с параметром--help
.В моём случае получилось нечто подобное:
Если в выдаче у Вас не будет параметра
-b
, это означает, что Ваш PHP собран без поддержки FastCGI.PS - Вам в любом случае было бы неплохо обновить PHP.
Обновил php.
А теперь такая проблема.При обращение к php-файлам nginx выдает
No input file specified.
Как решить эту проблему?
У Вас проблема с
fastcgi_param SCRIPT_FILENAME
.Допустим, что Ваш сайт живет в
C:\mysites\mysites.com
(то есть этоDOCUMENT_ROOT
).В этом случае
SCRIPT_FILENAME
должен быть таким:fastcgi_param SCRIPT_FILENAME c:/mysites/mysites.com$fastcgi_script_name;
Спаибо вам.
Теперь все работает.
Проблема была в том,что fastcgi_param не понимает не относительный путь, не путь вида /cygdrive/c/nginx…
Добрый день уважаемый автор.
Я хочу обратиться к вам за помощью.
Перестал запускаться nginx, выдавая:
2008/08/20 17:23:30 [notice] 1072#0: using inherited sockets from “c:\nginx\html
”
2008/08/20 17:23:30 [emerg] 1072#0: invalid socket number “c:\nginx\html” in NGI
NX environment variable, ignoring the rest of the variable
и все. висит в памяти и ни туда ни сюда. что делать??? я в ужасе.
А работать надо.
Василий, ну никак у меня воспроизвести этот глюк не получается