Nginx: переадресация урлов без слеша на урлы со слешем



Что организовать на сайте 301 редирект для всех адресов без оканчивающегося слеша с учетом GET-параметры в блоге server необходимо использовать следующую конфигурацию:
location / {
    try_files $uri $uri/ /index.php?$args;
    rewrite ^([^.\?]*[^/])$ $1/ permanent;
}

413 Request Entity Too Large

Ошибка вылазит при попытке загрузить на сервер файл больше одного мегабайта, причина тому client_max_body_size, который по умолчанию, если не указывать в конфиге, всегда равен 1 мегабайту. Лечится просто, открываем конфиг nginx и дописываем в конфиге /etc/nginx/nginx.conf директиву http {
следующий параметр:
client_max_body_size 2m;

Модуль nginx для авторизированного доступа к файлам большого объема

Часто возникает задача обеспечения авторизованного доступа к скачиванию пользователями статических файлов. Например, платная загрузка mp3, фильмов, или скачивание дилерами прайс-листов с индивидуальными ценами. Естественно, отдавать большие файлы php неоправданно, с этой задачей гораздо лучше справляется nginx.

Сложность в том, что информация об авторизированном пользователе хранится в сессии php, а обращение должно идти напрямую к nginx. При этом, доступ желательно проверять не отвлекая лишний раз пользователя. То есть авторизация по логину и паролю, или получение пользователем специальных ключей исключены.

Для решения этой задачи я и разработал модуль access_key. Модуль блокирует доступ, если url документа не содержит корректного ключа. Ключ генерируется в php и представляет собой md5 или sha1 хэш сигнатуры клиента, состоящей из некоего секретного слова и любых доступных в nginx переменных — ip адреса клиента, пути к файлу, etc.

Пример конфигурации:
location /download {
 accesskey on;

 accesskey_hashmethod md5;
 accesskey_arg «key»;
 accesskey_signature «mypass$remote_addr»;
}


Клиенту можно выдать ссылку вида example.com/download/file.zip?key=09093abeac094, которая будет доступена только с определенного адреса.

В текущей версии ключ может быть только GET параметром, возможность использования cookie планируется. При чем в случае применения с Suhosin украденный cookie будет бесполезен.

Пользуйтесь, как говорится, на здоровье, и не нагружайте по чем зря ваш php :)

Рабочий конфиг для DLE 9.3 под Nginx или переписаная портянка реврайтов.

По работе, периодически приходиться заниматься переносом уже работающих ресурсов с веб сервера Apache на связку nginx — fastcgi, или со старого сервера на новый с уже установленным веб сервером nginx. Ни в коем разе не допускаю мысли, что Apache плохой веб сервер, учитывая количество модулей, он очень универсален, можно решать практически любые задачи, но его монстрообразность предъявляет определенные требования к ресурсам системы.

Когда речь идет о VPS серверах, мы как раз имеем дело с жестко ограниченными ресурсами и работающий в системе apache уж никак их не экономит, ограничение его аппетитов и выкидывание неиспользуемых модулей, зачастую не является решением проблемы. Доходит иногда до того, что из-за нехватки ресурсов, сам процесс удаленного администрирования сервера через SSH, становится просто мучением, про работу сайтов вообще молчу. Nginx-же в силу своей архитектуры и мизерного потребления ресурсов наоборот, незаменим там, где высокие нагрузки. Поэтому первым шагом по оптимизации VPS серверов, как правило является установка Nginx — php-fastcgi ( естественно это не панацея, и дело не всегда в apache и его аппетитах ).

DLE — достаточно распространенный сайтовый движок, уверен у него масса достоинств и плюшек, но с точки зрения администратора, куда больше впечатляет выдающаяся портянка RewriteRule в файле .htaccess, идущим в поставке движка.
Поскольку nginx, в принципе, не поддерживает обработку файлов .htaccess, все эти правила приходится портировать в конфиг сервера Nginx, естественно с учетом его особенностей и синтаксиса.

В общем приведу основную часть рабочего конфига:

server {
    listen       IP_ADDR:80;
    server_name HOST;

    root /path/to/root;

    rewrite ^/page/(.*)$ /index.php?cstart=$1 last;

    location / {
        rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})(/?)+$" /index.php?year=$1&month=$2&day=$3 last;
        rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/page/([0-9]+)(/?)+$" /index.php?year=$1&month=$2&day=$3&cstart=$4 last;
        rewrite "^/([0-9]{4})/([0-9]{2})(/?)+$" /index.php?year=$1&month=$2 last;
        rewrite "^/([0-9]{4})/([0-9]{2})/page/([0-9]+)(/?)+$" /index.php?year=$1&month=$2&cstart=$3 last;
        rewrite "^/([0-9]{4})(/?)+$" /index.php?year=$1 last;
        rewrite "^/([0-9]{4})/page/([0-9]+)(/?)+$" /index.php?year=$1&cstart=$2 last;
        rewrite "^/([^.]+)(/?)+$" /index.php?do=cat&category=$1 last;
        rewrite "^/([^.]+)/page/([0-9]+)(/?)+$" /index.php?do=cat&category=$1&cstart=$2 last;
        index  index.php index.html index.htm;
    }

    location /tags/ {
        rewrite ^/tags/([^/]*)(/?)+$ /index.php?do=tags&tag=$1 last;
        rewrite ^/tags/([^/]*)/page/([0-9]+)(/?)+$ /index.php?do=tags&tag=$1&cstart=$2 last;
    }

    location /user/ {
        rewrite ^/user/([^/]*)/rss.xml$ /engine/rss.php?subaction=allnews&user=$1 last;
        rewrite ^/user/([^/]*)(/?)+$ /index.php?subaction=userinfo&user=$1 last;
        rewrite ^/user/([^/]*)/page/([0-9]+)(/?)+$ /index.php?subaction=userinfo&user=$1&cstart=$2 last;
        rewrite ^/user/([^/]*)/news(/?)+$ /index.php?subaction=allnews&user=$1 last;
        rewrite ^/user/([^/]*)/news/page/([0-9]+)(/?)+$ /index.php?subaction=allnews&user=$1&cstart=$2 last;
        rewrite ^/user/([^/]*)/news/rss.xml(/?)+$ /engine/rss.php?subaction=allnews&user=$1 last;
    }

    location /lastnews/ {
        rewrite ^/lastnews/(/?)+$ index.php?do=lastnews last;
        rewrite ^/lastnews/page/([0-9]+)(/?)+$ /index.php?do=lastnews&cstart=$1 last;
    }

    location /catalog/ {
        rewrite ^/catalog/([^/]*)/rss.xml$ /engine/rss.php?catalog=$1 last;
        rewrite ^/catalog/([^/]*)(/?)+$ /index.php?catalog=$1 last;
        rewrite ^/catalog/([^/]*)/page/([0-9]+)(/?)+$ /index.php?catalog=$1&cstart=$2 last;
    }

    location /newposts {
        rewrite ^/newposts(/?)+$ /index.php?subaction=newposts last;
        rewrite ^/newposts/page/([0-9]+)(/?)+$ /index.php?subaction=newposts&cstart=$1 last;
    }

    location /favorites {
        rewrite ^/favorites(/?)+$ /index.php?do=favorites last;
        rewrite ^/favorites/page/([0-9]+)(/?)+$ /index.php?do=favorites&cstart=$1 last;
    }

    location ~ \.(html|xml) {
        rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/page,([0-9]+),([0-9]+),(.*).html(/?)+$" /index.php?subaction=showfull&year=$1&month=$2&day=$3&news_page=$4&cstart=$5&news_name=$6 last;
        rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/page,([0-9]+),(.*).html(/?)+$" /index.php?subaction=showfull&year=$1&month=$2&day=$3&news_page=$4&news_name=$5 last;
        rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/print:page,([0-9]+),(.*).html(/?)+$" /engine/print.php?subaction=showfull&year=$1&month=$2&day=$3&news_page=$4&news_name=$5 last;
        rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/(.*).html(/?)+$" /index.php?subaction=showfull&year=$1&month=$2&day=$3&news_name=$4 last;
        rewrite "^/([^.]+)/page,([0-9]+),([0-9]+),([0-9]+)-(.*).html(/?)+$" /index.php?newsid=$4&news_page=$2&cstart=$3 last;
        rewrite "^/([^.]+)/page,([0-9]+),([0-9]+)-(.*).html(/?)+$" /index.php?newsid=$3&news_page=$2 last;
        rewrite "^/([^.]+)/print:page,([0-9]+),([0-9]+)-(.*).html(/?)+$" /engine/print.php?news_page=$2&newsid=$3 last;
        rewrite "^/([^.]+)/([0-9]+)-(.*).html(/?)+$" /index.php?newsid=$2 last;
        rewrite "^/page,([0-9]+),([0-9]+),([0-9]+)-(.*).html(/?)+$" /index.php?newsid=$3&news_page=$1&cstart=$2 last;
        rewrite "^/page,([0-9]+),([0-9]+)-(.*).html(/?)+$" /index.php?newsid=$2&news_page=$1 last;
        rewrite "^/print:page,([0-9]+),([0-9]+)-(.*).html(/?)+$" /engine/print.php?news_page=$1&newsid=$2 last;
        rewrite "^/([0-9]+)-(.*).html(/?)+$" /index.php?newsid=$1 last;
        rewrite "^/static/(.*).html(/?)+$" /index.php?do=static&page=$1 last;
        rewrite ^/rules.html$ /index.php?do=rules last;
        rewrite ^/statistics.html$ /index.php?do=stats last;
        rewrite ^/addnews.html$ /index.php?do=addnews last;
        rewrite ^/([^.]+)/rss.xml$ /engine/rss.php?do=cat&category=$1 last;
        rewrite ^/page,([0-9]+),([^/]+).html$ /index.php?do=static&page=$2&news_page=$1 last;
        rewrite ^/print:([^/]+).html$ /engine/print.php?do=static&page=$1 last;
        rewrite ^/rss.xml$ /engine/rss.php last;
        rewrite ^/sitemap.xml$ /uploads/sitemap.xml last;
    }

    location ~* \.(jpg|jpeg|gif|png|ico|swf|css|js)$ {
        expires             30d;
        add_header          Cache-Control public;
    }

## Тут установлен дополнительный пароль на админку
    location =/admin.php {
        auth_basic            "closed section";
        auth_basic_user_file  htpasswd;
        fastcgi_pass   unix:/tmp/fastcgi.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

    location ~ \.php$ {
        fastcgi_pass   unix:/tmp/fastcgi.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }

    location ~ /\.ht {
        deny  all;
    }
}


Конфиг проверен и работает на нескольких серверах, тем не менее не советую бездумно копировать в рабочий конфиг, например у вас могут быть установлены какие-нить плагины, для которых нужно индивидуально писать рерайты.

Неработает "последние новости" в DLE(NGINX)

Не так давно обнаружилось, что не работает ссылка на карту сайта, в связи с переходом на nginx, движок DLE. Что было исправлено дописывание в уже существующую строчку одного слова -last

Сегодня обнаружилась ещё одна проблема, не работали странички перехода по страницам в «последних новостях» в DLE
В своём конфиге site-avaible строчек указывающие на lastnews я вобще не обнаружил.
Решил проблему добавлением:
location /lastnews/ {
        rewrite ^/lastnews/(/?)+$ index.php?do=lastnews last;
        rewrite ^/lastnews/page/([0-9]+)(/?)+$ /index.php?do=lastnews&cstart=$1 last;
    }

После чего перезагрузил nginx и всё заработало.

Nginx не работает ссылка на sitemap (DLE)

DLE
После перевода движка DLE на nginx, пришлось отказаться от реврайтовских деревктив Apache. Написать аналог им для Nginx.

Но выяснилось, что ссылка на sitemap.xml так и не работала.

Для этого в nginx.conf
Заменить
rewrite ^/sitemap.xml$ uploads/sitemap.xml;

на
rewrite ^/sitemap.xml$ /uploads/sitemap.xml last;


Или просто добавить, если такой не было.

Запрет выполнения php в определённых директориях (nginx) или как защитить папки uploads в DLE(nginx)

Если ваш веб-сервер Apache, вы можете добавить следующие строки или файл .htaccess их уже имеет:

<Directory /website/attachments>
php_flag engine off
</Directory>


Чтобы отключить выполнение php в определённой директории на Nginx:
location /upload/ {
location ~ .*\.(php)?$
{
deny all;
}
}

Внимание: Если файл PHP находится в подкаталоге одного из этих каталогов, она все равно выполняет

Другой вариант запрета с перечислением каталогов и файлов:
location ~* ^/(upload|images)/.*\.(php|php5)$
{
deny all;
}


Если ваш веб-сервер lighthttpd вы можете сделать то же самое:

$HTTP["url"] =~ "^/(forumdata|templates|customavatars?)/" {
fastcgi.server = ()
}
Apache:
<Location "/forumdata">
php_admin_flag engine off
Options -ExecCGI
AddType text/plain .html .htm .shtml .php
</Location>

504 Gateway Time-out (nginx и в связке nginx+apache) - что можно сделать?

504 — значит скрипт (бэкенд) слишком долго отвечал или скрипт завершается раньше, чем получен ответ.

Сразу предупрежу, если вы получаете такое сообщение на обычном shared хостинге – то проблема скорее в том, что исполняемый скрипт не укладывается во временные рамки (30 или 60 секунд – в зависимости от настройки).

Это может быть по разным причинам:
  • объем данных, обрабатываемых скриптом существенно вырос
  • скрипт ображается к другим сайтам или сервисам, которые долго фомрируют ответ
  • скрипт слишком тяжелый

504 Gateway Timeout (time out) на чистом nginx

В случае с выделеными серверами:
в php.ini увеличить значение параметра
PHP max_execution_time

в конфиге nginx увеличить время ожидания исполнения скрипта:

proxy_read_timeout 120;
        proxy_connect_timeout 120;


увеличить оперативной памяти
Файл конфигурации nginx.conf находиться в каталоге /etc/nginx/

syntax: proxy_connect_timeout время
default: proxy_connect_timeout 60
context: http, server, location
Директива задаёт таймаут для соединения с проксированным сервером. Необходимо иметь в виду, что этот таймаут __не может быть больше 75 секунд__.


504 Gateway Timeout (time out) в связке nginx+apache


Если возникла ошибка 504 Gateway Timeout (time out) в связке nginx+apache то увеличим на сервере допустимое время выполнения скриптов и ожидания ответа:
php.ini:
max_execution_time = 900


nginx.conf:
proxy_read_timeout  900;
client_header_timeout  10m;
client_body_timeout    10m;
send_timeout           10m;


Теперь есть 900 секунд (15 минут) на выполнение скриптов.

Также:
worker_processes 2; количество worker-ов, обычно один.
keepalive_timeout 400; (было 100)

Как определить количество рабочих процессов, задаваемых параметром worker_processes?

Ответ автора nginx Игоря Сысоева:

Если весь сайт помещается в память сервера, к диску обращений нет, и это выделенный сервер для nginx, то 1. Не будет лишних переключений контекста. Если нужно ходить на диск, то 5-10 — это позволит обрабатывать соединения процессами, незаблокироваными на диске.

Кроме этого необходимо понаблюдать за состоянием процессов nginx в работе в часы пик. Командой ps посмотреть состояние рабочих процессов (worker process):

# ps ax -o %cpu,vsz,wchan,command | grep "nginx\|PID"

%CPU   VSZ WCHAN  COMMAND
0,0  1428 pause  nginx: master process /usr/local/nginx/sbin/nginx
0,0  2284 -      nginx: worker process (nginx)
0,0  2128 kqread nginx: worker process (nginx)

Если один из рабочих процессов находится в состоянии ожидания «kqread» в колонке «WCHAN», то значит их количество достаточно. Ну а если уж все они постоянно находятся в этом состоянии, то их количество можно сократить до одного.

И не забывайте контролировать логи ошибок nginx, если количество соединений превысит значение, которое в может обслужить nginx текущим количеством процессов, то в логах это будет соответствующее сообщение.

Прозрачное кэширование в nginx для всех и каждого

Представим, что у нас есть сайт, на который регулярно дают ссылки с хабра.
Нам нужно подготовить его к резким всплескам посещаемости. Как это сделать?

С версии 0.8.46 в nginx появились опции, позволяющие легко и просто настроить прозрачное кэширование для анонимных пользователей.

Для работы этой схемы от сайта требуется очень мало: достаточно лишь не начинать сессию, то есть не отправлять сессионную куку, без явной на то необходимости. Редкий сайт нельзя довести до такого состояния, а значит большинство сайтов можно защитить от резких всплесков посещаемости с помощью nginx с минимальными затратами сил и времени.

Научим сайт начинать сессию только когда она нужна


Для широкораспространенных сайтов на PHP это можно сделать выставив в ноль session.auto_start в php.ini или, лучше, в настройках виртуального хоста Apache. Также нам нужно задать подходящие имя для сессионной куки:

php_admin_value session.name "session"
php_admin_value session.auto_start 0


Читать дальше →

Атаки конкурентов, способные нанести вред вашему сайту: Классический DDOS веб-сервера

К наиболее популярным атакам без сомнения можно отнести этот популярный способ, когда веб-сервер переполняется запросами от ботов и не может нормально обслуживать запросы посетителей.
В результате такой атаки сайт становится недоступным, а с серверной стороны можно наблюдать рост нагрузки на сервер, большое число процессов веб-сервера и конечно, значительный рост коннектов. Если атака будет очень сильной и паразитный трафик превысит пропускную способность канала к серверу, то на него нельзя будет зайти по ssh.
Но столь сильные атаки — редкость, так как 10-20Mbps такого трафика вполне способны свалить неподготовленный сервер.

Способов борьбы с такими атаками очень много, как программных так и аппаратных. Кроме этого существуют отдельные сервисы, которые «чистят» трафик, присылая только легальные запросы.
Я же хочу рассмотреть очень простой и элегантный способ борьбы, для которого нам потребуется nginx и iptables с модулем string.
Читать дальше →