Comments 44
post_action обрабатывается синхронно, лучше скомпилить openresty и отправлять запросы через lua request
Немного критики:
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'ом.

Завтра на свежую голову еще раз перечитаю, что не бросилось в глаза.
Надо обязательно добавить инфу о том, что если у нас есть поддомены (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;

хотя про кеш сессий в статье написано, чуть позже заметил :)
Не стоит. Использование 4096-битного ключа, замедляет работу сервера, пусть и все же не прям существенно, но неприятно.
И не поддерживается jvm (включая актуальную 8u66). Стоит учитывать, если используются java-клиенты.
Спасибо за замечание! «Используются»!
Другое дело, что когда я последний раз генерил dhparam, я сделал 2048 вариант, так как генерация 4096 заняла целую вечность, которая была прервана ctrl+C.
Вот все-таки не советую использовать try_files. Были уже случаи когда и исходники отдавалиь и конфиги.
Тогда уж лучше не советовать использовать любой неправильно настроенный web-сервер, случаи то разные бывали ) Что опасного именно в try_files?
Спасибо за статью. Мне кажется в ней не хватает информации по gzip_static.
Мое дополнение к статье, касаемо безопасности:
# Запрет на открытие в 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;
location ~ /\. {
      deny all;
}


Надо быть аккуратным, т.к. под это правило попадают и директории. Допустим в битриксовых проектах достаточно часто можно встретить директории .default, доступ к которым будет ограничен, при том, что на страницах есть статичные файлы находящиеся в данной директории.
При «защите от сканеров» лучше отдавать не 403 код, а использовать nginx'овый 444 (reset connection).
К конфигу 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;
        }



Решает очень много проблем. :-)
location ^/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).
А для Битрикса и UMI.CMS есть у кого-нибудь бест практикс? Особенно первый интересует
Поддерживаю, очень интересно посмотреть на хороший конфиг для битрикс.
Нет ничего проще:

Файл /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;
}

Все работает на нескольких десятках сайтов и довольно быстро.
Для Битрикс есть bitrix_env который в 3 компанды разворачивается на CentOS. Там оптимальные настройки nginx для этой CMS.
Это прекрасно, вот только там ставится связка apache2+nginx, в случае работы на голом nginx приходится учитывать множество дополнительных вопросов.
Мне кажется, что стоило бы на GitHub в README не полотнищем все конфиги перечислять, а сделать нормальный репозитарий и все по папочкам разложить.
А вот такое стоит использовать?

location ~ \.php$ {
  fastcgi_split_path_info ^(.+.php)(.*)$;
  ...
}
1. Нет в 2.0 никакого protected.
2. В вебрут приложения не заливаются, так что защищать framework не надо.
3. views и themes никогда в вебруте не лежат.
4. location для php написан ужасно. Не нужен уже сто лет как fastcgi_split_path_info.
5. Не факт, что используется сокет для fpm.
В конфиге для Laravel не должно быть location /vendor/
В конфиге для Symfony для прода или стейджинга (если судить по / через app.php) не должно быть двух локаций для app_dev.php и app\.php.php

Нельзя гуглить конфиги, объединять их «чтобы работало» и использовать. Нужно понимать, для чего каждая строчка.
И для прода и для дева конфиги будут разные.
В конфиге для Symfony для прода или стейджинга (если судить по / через app.php) не должно быть двух локаций для app_dev.php и app\.php.php


На проде для app_dev.php лишняя (хотя у нас он на прод даже не деплоится, так что она именно лишняя), но вот для стйджинга обе полезны — на app.php (через «реврайт») приемочное тестирование проводится, а на app_dev.php первичная локализация и диагностика выявленных проблем.
Я уже когда-то приводил тут свой конфиг для 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;
}

А я решил остановиться на ентерпрайз версии и пока заморозился на 8.1, и вместо apcu у меня редис.
Блин. Буду ковырять логи. За конфиги спасибо. Хотя мой owncloud не публичный. Только для нашей лаборатории. Но хостится дома. Пусть будет)
Спасибо за статью, «наразал» ее на карточки Anki, выложил сюда — https://github.com/litnimax/anki/
Вопрос немного не по теме — есть локальное приложение (клиент) способное установить только HTTP соединение с удалённым web сервисом и далее обмен SOAP. А нужно, чтобы оно поддерживало отправлку по HTTPS.
Для такого NGINX пойдёт в качестве TLS прокси клиента? Т.е. локальное приложение знакет только про localhost, проброску вызова на https://somwhere/../… делает NGINX.
Вот это — https://www.nginx.com/resources/admin-guide/nginx-https-upstreams/
Что значит "та сторона nginx приняла"? К той стороне должен прийти «обычный» HTTPS запрос, какая разница кто его отправил.

Та сторона может требовать наличия клиентского сертификата, подписанного ЦА, которому она доверяет. "Obtaining an SSL Client Certificate" по вашей ссылке.

Only those users with full accounts are able to leave comments. Log in, please.