Доброго времени суток, уважаемые хабравчане! В Elasticweb мы негласно ратуем за Nginx и, наверное, мы одни из немногих хостингов, которые не поддерживают Apache и .htaccess соответственно. В связи с этим, большое количество обращений в тех. поддержку связано с оказанием помощи в написании конфигурационного файла для Nginx. Поэтому мы решили собрать коллекцию полезных сниппетов и коллекцию готовых Nging конфигов для наиболее популярных CMS/CMF/Фреймворков на PHP.
Готовые конфиги:
- Asgard CMS
- Bolt CMS
- CMS Made Simple
- Codeigniter
- Data Life Engine
- Drupal 7, 8
- FuelPHP
- Joomla 2, 3
- KodiCMS
- Kohana
- Laravel
- MaxSite CMS
- MediaWiki
- MODx Revolution
- Octobercms
- OpenCart 1.5
- phpBB3
- ProcessWire 2
- Symfony
- Wordpress 4
- Yii Advanced
- Yii Basic
- ZenCart 1.5
- Zend Framework
Команды Nginx
Основные команды для выполнения базовых операций во время работы Nginx.
- nginx -V — проверить версию Nginx, его скомпилированные параметры конфигурации и установленные модули.
- nginx -t — протестировать конфигурационный файл и проверить его расположение.
- nginx -s reload — перезапустить конфигурационный файл без перезагрузки Nginx.
Location блок на PHP
Простой шаблон для быстрой и легкой установки PHP, FPM или CGI на ваш сайт.
location ~ \.php$ {
try_files $uri =404;
client_max_body_size 64m;
client_body_buffer_size 128k;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/path/to/php.sock;
}
Rewrite и Redirection
Force www
Корректный способ определить удаленный сервер по домену без www и перенаправить его c www:
server {
listen 80;
server_name example.org;
return 301 $scheme://www.example.org$request_uri;
}
server {
listen 80;
server_name www.example.org;
...
}
Также работает для HTTPS.
Force no-www
Корректный способ определить удаленный сервер по домену c www и перенаправить его без www:
server {
listen 80;
server_name example.org;
}
server {
listen 80;
server_name www.example.org;
return 301 $scheme://example.org$request_uri;
}
Force HTTPS
Способ для переадресации с HTTP на HTTPS:
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
# let the browsers know that we only accept HTTPS
add_header Strict-Transport-Security max-age=2592000;
...
}
Force Trailing Slash
Данная строка добавляет слэш
/
в конце каждого URL, только в том случаее если в URL нет точки или параметров. Тоесть после example.com/index.php или example.com/do?some=123 слэш не поставится. rewrite ^([^.\?]*[^/])$ $1/ permanent;
Редирект на страницу
server {
location = /oldpage.html {
return 301 http://example.org/newpage.html;
}
}
Редирект на сайт
server {
server_name old-site.com
return 301 $scheme://new-site.com$request_uri;
}
Редирект на определенный путь в URI
location /old-site {
rewrite ^/old-site/(.*) http://example.org/new-site/$1 permanent;
}
Производительность
Кэширование
Навсегда разрешить браузерам кэшировать статические содержимое. Nginx установит оба заголовка: Expires и Cache-Control.
location /static {
root /data;
expires max;
}
Запретить кэширование браузерам (например для отслеживания запросов) можно следующим образом:
location = /empty.gif {
empty_gif;
expires -1;
}
Gzip сжатие
gzip on;
gzip_buffers 16 8k;
gzip_comp_level 6;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
gzip_types
text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
text/javascript application/javascript application/x-javascript
text/x-json application/json application/x-web-app-manifest+json
text/css text/plain text/x-component
font/opentype application/x-font-ttf application/vnd.ms-fontobject
image/x-icon;
gzip_disable "msie6";
Кэш файлов
Если у вас кешируется большое количество статических файлов через Nginx, то кэширование метаданных этих файлов позволит сэкономить время задержки.
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
SSL кэш
Подключение SSL кэширования позволит возобновлять SSL сессии и сократить время к следующим обращениям к SSL/TLS протоколу.
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
Поддержка Upstream
Активация кеширования c использованием Upstream подключений:
upstream backend {
server 127.0.0.1:8080;
keepalive 32;
}
server {
...
location /api/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
Мониторинг
По умолчанию Stub Status модуль не собирается, его сборку необходимо разрешить с помощью конфигурационного параметра —with-http_stub_status_module и активировать с помощью:
location /status {
stub_status on;
access_log off;
}
Данная настройка позволит вам получать статус в обычном текстовом формате по общему количеству запросов и клиентским подключениям (принятым, обработанным, активным).
Более информативный статус от Nginx можно получить с помощью Luameter, который несколько сложнее в установке и требует наличия Nginx Lua модуля. Это предоставит следующие метрики по различным конфигурационным группам в формате JSON:
- Общее количество запросов/ответов.
- Общее количество ответов сгруппирированных по статус кодам: 1xx, 2xx, 3xx, 4xx, 5xx.
- Общее количество байт принятых/отправленных клиенту.
- Промежуточные отрезки времени для оценки минимума, максимума, медианы, задержек и тд.
- Среднестатистическое количество запросов для простоты мониторинга и составления прогнозов по нагрузке.
- И прочее…
Пример дашборда от Luameter.
Также для сбора статистики отлично подходит ngxtop.
Безопасность
Активация базовой аунтификации
Для начала вам потребуется создать пароль и сохранить его в обычной текстовом файле:
имя:пароль
Затем установить найтройки для server/location блока, который необходимо защитить:
auth_basic "This is Protected";
auth_basic_user_file /path/to/password-file;
Открыть только локальный доступ
location /local {
allow 127.0.0.1;
deny all;
...
}
Защита SSL настроек
- Отключить SSLv3, если он включен по умолчанию. Это предотвратит POODLE SSL Attack.
- Шифры, которые наилучшим образом обеспечат защиту. Mozilla Server Side TLS and Nginx.
# don’t use SSLv3 ref: POODLE CVE-2014-356 - http://nginx.com/blog/nginx-poodle-ssl/ ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Ciphers set to best allow protection from Beast, while providing forwarding secrecy, as defined by Mozilla (Intermediate Set) - https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; ssl_prefer_server_ciphers on;
Прочее
Подзапросы после завершения
Бывают ситуации, когда вам необходимо передать запрос на другой бэкэнд в дополнении или после его обработки. Первый случай — отслеживать количество завершенных загрузок путем вызова API, после того как пользователь скачал файл. Второй случай -отслеживать запрос, к которому вы бы хотели вернуться как можно быстрее (возможно с пустым .gif) и сделать соответствующие записи в фоновом режиме. post_action, который позволяет вам определить подзапрос и будет отклонен по окончанию текущего запроса — является лучшим решением для обоих вариантов.
location = /empty.gif {
empty_gif;
expires -1;
post_action @track;
}
location @track {
internal;
proxy_pass http://tracking-backend;
}
Распределение ресурсов между источниками
Самый простой и наиболее известный способ кросс-доменного запроса на ваш сервер:
location ~* .(eot|ttf|woff) {
add_header Access-Control-Allow-Origin *;
}
Источники
Большое спасибо всем за внимание!
Комментарии (39)
nikitasius
04.12.2015 01:59+10Немного критики:
gzip_comp_level 6;
habrahabr.ru/post/99256, там показано падения производительности. Лично я использую 1. В современном мире, с терабайтами траффика и сервисом от cloudflare резонее обслуживать бОльшее число запросом, чем экономить проценты от текста в сравнении с 1й компрессией.
Открыть только локальный доступ
Помимо 127.0.0.1 надо указать внешний ipv4 адресс сервера и, при наличии ipv6 его внешний ipv6 и, конечно же, ::1. Иначе в случае обращения на example.com/api через тот же ssi (include virtual) будет облом.
add_header Strict-Transport-Security max-age=2592000;
Надо обязательно добавить инфу о том, что если у нас есть поддомены (cdn, api, etc) которые работают в режиме https, то надо указатьincludeSubDomains
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
Теперь минус — вы забыли про ssl_dhparam! Генерируем свой на 2048 (или если есть много времени, то на 4096)!
Далее в вашей статье рассмотрена тема php (или любой другоу бекенд через fastcgi или proxypass), но не рассмотрена тема кеша и ключей (fastcgi_cache_key/proxy_cache_key), и в особенности таких вкусностей как fastcgi_cache_lock/proxy_cache_lock и fastcgi_cache_lock_timeout/proxy_cache_lock_timeout, и как это шикарно помогает с наплывом ботой или клиентов.
Для начала вам потребуется создать пароль и сохранить его в обычной текстовом файле:
имя: пароль
Используем пароли после обработки crypt'ом.
Завтра на свежую голову еще раз перечитаю, что не бросилось в глаза.InSys
04.12.2015 03:14+2Надо обязательно добавить инфу о том, что если у нас есть поддомены (cdn, api, etc) которые работают в режиме https, то надо указать includeSubDomains
Тут двояко. Указывать
add_header Strict-Transport-Security «max-age=31536000; includeSubDomains»;includeSubDomains
стоит только если вы уверены что ВСЕ ваши поддомены работают по HTTPS.
или если есть много времени, то на 4096
Не стоит. Использование 4096-битного ключа, замедляет работу сервера, пусть и все же не прям существенно, но неприятно.
От себя добавлю, у себя я про это писал, хорошо при всем прочем включать поддержку OSCP ответов, кеш сессий, иssl_prefer_server_ciphers
, т.е. что то вот в таком духе:
ssl_session_timeout 1h; ssl_session_cache shared:SSL:16m; ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate "[...].trusted.crt"; resolver 8.8.4.4 8.8.8.8 valid=300s; resolver_timeout 10s; ssl_prefer_server_ciphers on;
хотя про кеш сессий в статье написано, чуть позже заметил :)grossws
04.12.2015 04:34+3Не стоит. Использование 4096-битного ключа, замедляет работу сервера, пусть и все же не прям существенно, но неприятно.
И не поддерживается jvm (включая актуальную 8u66). Стоит учитывать, если используются java-клиенты.nikitasius
04.12.2015 11:07Спасибо за замечание! «Используются»!
Другое дело, что когда я последний раз генерил dhparam, я сделал 2048 вариант, так как генерация 4096 заняла целую вечность, которая была прервана ctrl+C.
dblokhin
04.12.2015 09:18+8Мое дополнение к статье, касаемо безопасности:
# Запрет на открытие в iframe add_header X-Frame-Options SAMEORIGIN; # Блокировка путей, начинающийся с точки (например, ".git", ".svn") location ~ /\. { deny all; } # Большинство хакерских сканеров if ( $http_user_agent ~* (nmap|nikto|wikto|sf|sqlmap|bsqlbf|w3af|acunetix|havij|appscan) ) { return 403; }
В качестве оптимизации дисковой нагрузки, можно упомянуть отключение логов:
# Контексте сервера access_log off; # В контексте статики location ~* \.(pdf|gif|jpg|css|txt|js|png|ico|svg|ttf|woff|eot|html|htm|json|woff2)$ { root /www/path/static; log_not_found off; }
Всем известный скрипт для gzip_static:
for i in `find ./* -type f -name '*.js'`; do echo $i; gzip -c -9 $i > $i.gz; done; for i in `find ./* -type f -name '*.css'`; do echo $i; gzip -c -9 $i > $i.gz; done;
ilusha_sergeevich
04.12.2015 09:46+1Спасибо большое. Мне добавить или пулреквест сделаете?
dblokhin
04.12.2015 17:32Конечно, добавляйте.
script88
08.12.2015 18:04+1location ~ /\. { deny all; }
Надо быть аккуратным, т.к. под это правило попадают и директории. Допустим в битриксовых проектах достаточно часто можно встретить директории .default, доступ к которым будет ограничен, при том, что на страницах есть статичные файлы находящиеся в данной директории.
Alukardd
04.12.2015 10:50+4При «защите от сканеров» лучше отдавать не 403 код, а использовать nginx'овый 444 (reset connection).
donchenko
04.12.2015 10:21К конфигу Wordpress предлагаю добавить:
location ~* /(?:uploads|files)/.*\.php$ { deny all; } location ^wp-includes/(.*).php { deny all; } location ^/wp-admin/includes(.*)$ { deny all; } location ~ wp-config.php { deny all; }
Решает очень много проблем. :-)nikitasius
04.12.2015 11:27+1location ^/wp-admin/includes(.*)$ { deny all; }
Как минимум на весьwp-admin
поставитьauth_basic
, тогда можно будет спокойно работать в админке, пройдя две авторизации (nginx и wp).
У меня заметка параноика была, за истину не беру, есть несколько костылей (например доступ к запароленным статьям через ln к оригинальному) wp-login.
Глобально же, в случае с любой CMS следует ввести фильтрацию аргументов, например:
if ($args ~* "(eval|duplicate|base64|substring|preg_replace|create_function)") { return 403; }
Это понизит шанс SQL инъекции или других гадостей через адресную строку. Только надо понимать, использует ли сам движок некоторые их них. Если использует — это все очень просто обыгрывается через if'ы в nginx иauth_basic
(когда в конфигедватри if, первый на проверку авторизации, второй на проверку условия, а третий на проверку результата первых 2х и выдачу 403).
Vorchun
04.12.2015 11:09+1А для Битрикса и UMI.CMS есть у кого-нибудь бест практикс? Особенно первый интересует
andreios
04.12.2015 11:59+2Поддерживаю, очень интересно посмотреть на хороший конфиг для битрикс.
Sleuthhound
05.12.2015 21:00Нет ничего проще:
Файл /etc/nginx/common/location_bitrix.conf
location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|mov)$ {
expires 14d;
access_log off;
log_not_found off;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location ~* /upload/.*\.php$ {
return 403;
error_page 403 /403_error.html;
}
location ~ (/\.ht|/bitrix/modules|/upload/support/not_image) {
deny all;
access_log off;
log_not_found off;
}
location / {
charset $cs;
try_files $uri $uri/ cms;
open_file_cache max=1024 inactive=600s;
open_file_cache_valid 2000s;
open_file_cache_min_uses 1;
open_file_cache_errors off;
index index.php index.html index.htm;
}
Файл /etc/nginx/common/bitrix.conf
location ~ \.php$ {
try_files $uri cms;
charset $cs;
fastcgi_pass $fastcgipass;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
location cms {
charset $cs;
fastcgi_pass $fastcgipass;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/bitrix/urlrewrite.php;
fastcgi_param SCRIPT_NAME /bitrix/urlrewrite.php;
fastcgi_param QUERY_STRING REQUEST_URI=$uri&$args;
include /etc/nginx/fastcgi_params;
}
Файл вирт.хоста:
server {
listen XXX.XXX.XX.XX:80;
server_name domain.ru;
root /var/www/domain.ru/web;
index index.php index.html index.htm index.cgi index.pl index.xhtml;
error_log /var/log/httpd/domain.ru/error.log;
access_log /var/log/httpd/domain.ru/access.log main;
set $fastcgipass 127.0.0.1:9016;
set $cs utf-8;
include /etc/nginx/common/location_bitrix.conf;
include /etc/nginx/common/bitrix.conf;
}
Все работает на нескольких десятках сайтов и довольно быстро.
rhamdeew
08.12.2015 14:29Для Битрикс есть bitrix_env который в 3 компанды разворачивается на CentOS. Там оптимальные настройки nginx для этой CMS.
andreios
09.12.2015 12:51Это прекрасно, вот только там ставится связка apache2+nginx, в случае работы на голом nginx приходится учитывать множество дополнительных вопросов.
FractalizeR
04.12.2015 11:37+1Мне кажется, что стоило бы на GitHub в README не полотнищем все конфиги перечислять, а сделать нормальный репозитарий и все по папочкам разложить.
kacang
04.12.2015 11:54А вот такое стоит использовать?
location ~ \.php$ { fastcgi_split_path_info ^(.+.php)(.*)$; ... }
SamDark
04.12.2015 14:50Для Yii как-то шлак…
SamDark
04.12.2015 14:53+31. Нет в 2.0 никакого protected.
2. В вебрут приложения не заливаются, так что защищать framework не надо.
3. views и themes никогда в вебруте не лежат.
4. location для php написан ужасно. Не нужен уже сто лет какfastcgi_split_path_info
.
5. Не факт, что используется сокет для fpm.
OnYourLips
04.12.2015 17:18+1В конфиге для Laravel не должно быть location /vendor/
В конфиге для Symfony для прода или стейджинга (если судить по / через app.php) не должно быть двух локаций для app_dev.php и app\.php.php
Нельзя гуглить конфиги, объединять их «чтобы работало» и использовать. Нужно понимать, для чего каждая строчка.
И для прода и для дева конфиги будут разные.DYPA
04.12.2015 17:59вообще в случае symfony лучше было бы оставить такую ссылку symfony.com/doc/current/cookbook/configuration/web_server_configuration.html#nginx
VolCh
04.12.2015 20:00В конфиге для Symfony для прода или стейджинга (если судить по / через app.php) не должно быть двух локаций для app_dev.php и app\.php.php
На проде для app_dev.php лишняя (хотя у нас он на прод даже не деплоится, так что она именно лишняя), но вот для стйджинга обе полезны — на app.php (через «реврайт») приемочное тестирование проводится, а на app_dev.php первичная локализация и диагностика выявленных проблем.
Sleuthhound
04.12.2015 18:12Простите за серость, а где шаблоны то? В https://github.com/elasticweb/nginx-configs пусто.
J_o_k_e_R
05.12.2015 00:44+1Я уже когда-то приводил тут свой конфиг для owncloud, но с тех пор я его подчистил и проверил под версией 8.1, так что повторю еще раз. Отличительная особенность — у меня owncloud в поддиректории основного домена. Конфиг включен через:
location ^~ /cloud { include apps/cloud.conf; }
Не претендует в highload (у меня всякие limit_* в вышестоящем конфиге), но претендует на некоторый разумный уровень безопасности, который приятно дополняет php-fpm от отдельного пользователя и вдумчивые права на диру с owncloud, в том числе через acl.
Дира с данными у меня вынесена нафиг из вебрута.
headers.conf содержит общие директивы для SSL, совершенно верно тут приведенные. Ну плюс так же упомянутый dhparam.
Собственно конфигclient_max_body_size 5G; # set max upload size fastcgi_buffers 64 4K; index index.php; error_page 403 /cloud/core/templates/403.php; error_page 404 /cloud/core/templates/404.php; add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none; include headers.conf; location = /cloud/robots.txt { allow all; log_not_found off; access_log off; } location ^~ /cloud/data { internal; alias /var/www/mysite.org/clouddata/data; } location ~ ^/cloud/(data|config|\.ht|db_structure\.xml|README|occ) { deny all; } rewrite ^/cloud/go/(.+) /cloud/public.php?service=shorty_relay&id=$1 last; rewrite ^/cloud/caldav(.*)$ /cloud/remote.php/caldav$1 redirect; rewrite ^/cloud/carddav(.*)$ /cloud/remote.php/carddav$1 redirect; rewrite ^/cloud/webdav(.*)$ /cloud/remote.php/webdav$1 redirect; # The following 2 rules are only needed with webfinger rewrite ^/cloud/.well-known/host-meta /cloud/public.php?service=host-meta last; rewrite ^/cloud/.well-known/host-meta.json /cloud/public.php?service=host-meta-json last; rewrite ^/cloud/.well-known/carddav /cloud/remote.php/carddav/ redirect; rewrite ^/cloud/.well-known/caldav /cloud/remote.php/caldav/ redirect; rewrite ^(/cloud/core/doc/[^\/]+/)$ $1/index.html; try_files $uri $uri/ =404; location ~ ^(/cloud/(?:\w+\.php|core/templates/40\d\.php|apps/files_encryption/files/error.php|ocs/v1.php))(/.*)?$ { try_files $1 =404; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$1; fastcgi_param PATH_INFO $2; fastcgi_param HTTPS on; fastcgi_param MOD_X_ACCEL_REDIRECT_ENABLED on; fastcgi_param modHeadersAvailable true; #Avoid sending the security headers twice fastcgi_read_timeout 130s; fastcgi_pass unix:/run/php-fpm-cloud.socket; fastcgi_intercept_errors on; } location ~ \.php$ { deny all; #можно return 404; } # Optional: set long EXPIRES header on static assets location ~* ^.+\.(jpe?g|gif|bmp|ico|png|css|js|swf)$ { expires 30d; log_not_found off; }
x88
post_action обрабатывается синхронно, лучше скомпилить openresty и отправлять запросы через lua request