> Почтовики? STARTTLS? Вы случайно с NNTP/Usenet не попутали? В списках рассылки можно
> хоть из веб-интерефейса Gmail с двухфакторной авторизацией общаться.Нет не попутал. Отправить баг в postfix и dovecot через их маиллисты мне не представляется возможным. В Гугле, Яндексе и прочих зондах читающих Вашу почту и разрешающие общение с почтовиками без STARTTLS, можно, но я же PHP-ник, я ими не пользуюсь и никому не доверяю свою почту кроме своих почтовых серверов.
> index.html и так подставляется при запросе директории. А вот эмулировать поведение
> log_not_found off; с помощью try_files - это странно.
А зачем вообще тогда по умолчанию заносить каждую 404 в лог ошибок?
Я думал логика в том, что каждый отдаваемый nginx-ом должен проходить через try_files, и если он отдаёт каким-то иным способом то именно это должно отображаться в файлах ошибок, но Ваш ответ меня удивил.
> Обычно вся конфигурация сводится к следующим двум location-ам:
> Всё предельно просто: если файл есть на диске - мы его отдаем,
> а всё остальное отправляем в приложение. Приложение уже возьмет $_SERVER['REQUEST_URI']
> и распарсит как угодно.
Огромная Вам благодарность за этот пример!
Во-первых я хоть где-то увидел как используется именованный location, когда в документации упоминается только что он есть и он указывается через @.
Во-вторых мне и в голову раньше не приходило, что так вообще возможно. Не знаю, можно ли так в апаче было сделать?
Мой код теперь.
error_page 404 /en/error;
location = /index.php
{
return 404;
}
location ^~ /.
{
return 404;
}
location /
{
try_files $uri @php;
}
location @php
{
fastcgi_pass unix:/run/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
include fastcgi_params;
}
А на стороне PHP
<?php if(strpos($_SERVER['REQUEST_URI'],'?') > 0)
{
if(isset($_SERVER['REQUEST_URI'][59],$_GET['token'][19],$_GET['PayerID'][12]) && !isset($_SERVER['REQUEST_URI'][60]) && strpos($_SERVER['REQUEST_URI'],'/payment?') == 3)
$_SERVER['REQUEST_URI'] = substr($_SERVER['REQUEST_URI'],0,strpos($_SERVER['REQUEST_URI'],'?'));
else
{
unset($_GET,$_REQUEST);
$_SERVER['REQUEST_URI'] = substr($_SERVER['REQUEST_URI'],0,4).'error';
}
}
if(strpos($_SERVER['REQUEST_URI'],'/.') === false && strpos($_SERVER['REQUEST_URI'],'.php') === false)
{
$arr = explode('/',substr($_SERVER['REQUEST_URI'],1));
if(isset($arr[0][1]) && !isset($arr[0][2]))
{
$_GET['lang'] = $arr[0];
if(isset($arr[1]))
{
$_GET['view'] = $arr[1];
if(isset($arr[2]))
{
if($_GET['view'] == 'api')
{
if(substr($arr[2],-6) == '=.html')
{
$_GET['data'] = substr($arr[2],0,$p=strpos($arr[2],'='));
$_GET['iv'] = substr($arr[2],++$p,-6);
unset($p);
}
else $_GET['view'] = 'error';
}
elseif(substr($arr[2],-5) == '.html')
$_GET['id'] = substr($arr[2],0,-5);
else
{
$_GET['tab'] = $arr[2];
if(isset($arr[3]) && substr($arr[3],-5) == '.html')
$_GET['id'] = substr($arr[3],0,-5);
}
}
}
}
else $_GET['view'] = 'error';
unset($arr);
}
else $_GET['view'] = 'error';
Я полностью избавился от регулярок и от дублирования, не считая повторяющийся return 404;
При этом на стороне PHP это делать проще и удобнее, причём для этого мне не нужны регулярки вообще!
Этим кодом я почти доволен.
Осталось только логи отформатировать, статус прикрутить и кеширование со сжатием добавить.
Вот только одна особенность nginx-а и try_files доставила мне проблем.
У Apache 403 всегда сопровождается заголовком Access Denied и означает только это.
У nginx 403 сопровождается заголовком Forbidden и может означать что угодно. :-(
location /
{
index index.html;
try_files $uri $uri/ @php;
}
По моей логике при $uri / или /static/js/lib_name/doc/ (в doc есть index.html) алгоритм должен быть такой (эмулирую то, что в Apache по умолчанию)
1 Проверяет если файл $uri Да - отдаёт Нет - идёт дальше.
2 Проверяет есть ли файл $uri/index.html Да - отдаёт Нет - идёт дальше
3 Если дошло до этого шга, то запрос отправляется в PHP.
Но каково было моё удивление и недоумение получить 403 Forbidden в этой конфигурации и далеко не сразу понять почему и где...
оказывается, если $uri/index.html не существует - то никакой передачи дальше в PHP не происходит а nginx тупо выдаёт 403 Forbidden
В итоге приходится не использовать индексирование и указывать полный путь к /static/js/lib_name/doc/index.html и подобным докам не приятно, но жить с этим можно.
Почему в nginx так и зачем так было сделано?
Можно ли как-то просто и универсально исправить проблему и привести к желаемому поведению?
И ещё интересный вопрос, можно ли как-нибудь ещё вызывать именованые location кроме как из try_files? Если да, приведите, пожалуйста, примеры
> Программисты на других ЯП всю дорогу так и делали и только PHP-шники
> извращаются.
> А ещё правильнее - это всю статику положить в отдельную директорию. Посмотрите,
> к примеру, насколько просто выглядит конфигурация nginx для типичного сайта на
> Django:
> https://uwsgi.readthedocs.io/en/latest/tutorials/Django_and_...
Видимо потому-что программисты на других языках, занимаются не web-сайтами, в их классическом понимании, а всякими CRM-ками, встроенными интерфейсами, например для управления IP-камерами и прочими вещами не предназначенными для всеобщего обозрения.
По этому им не нужна ни безопасность, ни ЧПУ, ни индексирование в поисковиках, ни вообще чтобы кто-то про них знал и их находил.
В конфигурации на Django всё, казалось бы красиво, но вот незадача, она совершенно не знает про /robots.txt и /sitemap.xml Эти файлы пойдут лесом. А ещё файлы авторизации различных СЕО-ников для разных систем аналитики, которые в корень кладутся...
Мне, как админу серверов, нужно для каждого из них правило в nginx прописывать и каждый раз сервер передёргивать?
Знаю про то что для последнего можно DNS использовать, но давать СЕО-никам доступ к DNS ещё хуже.
А более, на мой взгляд, главное, эта конфигурация не защищает от отдачи скрытых файлов!
Например .git, или даже .directory,
Слышал, в Яндексе как раз и было такое, что у них их /.git/ спокойно nginx раздавал.
Так что не вижу ничего плохого в том, чтобы класть index.php в DOCUMENT_ROOT и запрещать к нему доступ, как и доступ ко всем скрытым файлам и директориям хотя бы в DOCUMENT_ROOT.
Это и удобнее и безопаснее, тем более у меня ущё со времён апача в нём всего одна строчка <?php require dirname(__DIR__).'/init.php';
Ещё для меня одним неприятным открытием было, когда я убрал index.php из DOCUMENT_ROOT и вызывал сразу init.php, у меня перестал читаться .user.ini файл, даже после того, как я продублировал его за DOCUMENT_ROOT, видимо для его чтения он должен находится только в DOCUMENT_ROOT вместе с вызываемым скриптом (точкой входа).
Так что пользы от выноса index.php из DOCUMENT_ROOT я не вижу, а проблемы появляются достаточно.
> REQUEST_URI не обязан как-либо пересекаться с SCRIPT_FILENAME. Это в Apache с mod_php
> и .htaccess иначе сложно, там выполняемый файл эквивалентен запрашиваемому URI. С
> FastCGI не так и нет смысла тащить костыли от mod_php на
> FastCGI конфигурацию.
Я это уже понял. Для этого я и веду с Вами этот публичный диалог, чтобы работать с nginx правильно, а не так, как я привык работать с Apache.
А также чтобы это потом пригодилось не только мне, но у другим... PHP-никам.
> Можно наблюдать Max Early Data: 16384
Благодарю, значит работает! :-)
>> Хотелось бы если не формул, то инструкций по тому как узнать что
>> процессов не хватает или их в избытке. Понимаю что эта тема
>> не Ваша, буду разбираться с этим дальше.
> Посмотрите для начала сколько у вас один процесс занимает памяти. Настраивать их
> количество больше, чем имеющейся свободной памяти на сервере - точно не
> стоит.
На сервере же не один PHP работает. Там и MariaDB ей тоже ОЗУ нужна под всякие индексы.
Redis обитает, кеширующий ответы от MariaDB, и тоже будет рад лишней ОЗУ.
Но с redis я понимаю и знаю как определять и контролировать его расход ОЗУ и выдаю ему столько, сколько нужно, но чтобы без переизбытка.
И у php-fpm, наверно есть какой-то разумный предел кол-ва процессов, больше которого ресурсы потребляет, но выигрыш в производительности не приносит.
Или ему нужно отдавать все свободные ресурсы сервера?