Skip to content

Instantly share code, notes, and snippets.

@ufth
Last active October 27, 2023 22:01
Show Gist options
  • Save ufth/02cf876800be1bb70411b19718b70dcf to your computer and use it in GitHub Desktop.
Save ufth/02cf876800be1bb70411b19718b70dcf to your computer and use it in GitHub Desktop.
безопасный конфиг nginx для wordpress с php-fpm chroot
# /etc/nginx/sites-available/example.com.conf
# if you find this config useful, please consider donating bitcoin:1MjxPWAyebHhjQdjLPGcV5oZd5VbEWrAga
# а это уже непосредственно сам файл виртуального хоста
# разрешаем 30 запросов за 60 секунд с одного ip
# этот параметр работает как ограничение скорости км/ч на знаке,
# *нельзя* сделать 30 запросов за несколько секунд и ждать следующей минуты
# такая запись эквивалентна разрешённой скорости в 0.5 запроса в секунду
limit_req_zone $binary_remote_addr zone=login_user:1m rate=30r/m;
# ограничиваем количество запросов к поиску сайта с одного ip
map $arg_s $is_search {
default "";
"~.*" $binary_remote_addr;
}
limit_req_zone $is_search zone=search_user:1m rate=10r/m;
upstream fcgi_user {
server 127.0.0.1:9001;
}
server {
listen 80;
# listen 443 ssl;
# ssl_session_cache shared:SSL:10m;
# ssl_session_timeout 10m;
# ssl_certificate /etc/nginx/ssl/example.com.crt;
# ssl_certificate_key /etc/nginx/ssl/example.com.key;
# add_header Strict-Transport-Security "max-age=31536000";
server_name example.com;
charset utf-8;
#error_log /var/log/nginx/e.example.log crit;
error_log /var/log/nginx/e.example.log error;
access_log /var/log/nginx/a.example.log wtimes buffer=16k flush=10s;
root /www/user/example.com/www;
index index.php index.html index.htm;
limit_req zone=search_user burst=1 nodelay;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_intercept_errors off; # передавать ли клиенту ответы FastCGI-сервера с кодом больше либо равным 400, или же перенаправлять их на обработку nginx’у с помощью директивы error_page.
#fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME /www$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT /www;
location / {
limit_except GET HEAD POST {
deny all;
}
try_files $uri $uri/ /index.php?$args;
}
# к этим скриптам ВСЕГДА должен быть доступ
location = /index.php {
limit_except GET HEAD POST {
deny all;
}
include snippet.d/sec_headers;
fastcgi_pass fcgi_user;
}
location = /wp-login.php {
limit_except GET HEAD POST {
deny all;
}
limit_req zone=login_user burst=1 nodelay;
include snippet.d/sec_headers;
fastcgi_pass fcgi_user;
}
location = /wp-includes/js/tinymce/wp-tinymce.php {
limit_except GET HEAD {
deny all;
}
include snippet.d/sec_headers;
fastcgi_pass fcgi_user;
}
location ~* ^/wp-admin/[^/]*\.php$ {
limit_except GET HEAD POST {
deny all;
}
include snippet.d/sec_headers;
client_max_body_size 16m;
try_files $fastcgi_script_name =404;
fastcgi_pass fcgi_user;
}
# к этим скриптам МОЖНО запретить доступ (нужно просто удалить блок)
location = /wp-cron.php {
limit_except GET HEAD POST {
deny all;
}
include snippet.d/sec_headers;
fastcgi_pass fcgi_user;
}
location = /wp-activate.php {
limit_except GET HEAD {
deny all;
}
include snippet.d/sec_headers;
fastcgi_pass fcgi_user;
}
location ~* ^/wp-content/plugins/.*\.php$ {
limit_except GET HEAD POST {
deny all;
}
include snippet.d/sec_headers;
try_files $fastcgi_script_name =404;
fastcgi_pass fcgi_user;
}
# иногда может понадобится разрешить интерпретацию этих файлов:
#
# /xmlrpc.php - если вы не работаете с сайтом через клиент на мобильном устройстве
# /wp-trackback.php - трэкбэки и пингбэки
# /wp-mail.php - если вы не шлёте сайту статьи на почту, откуда он их забирает
# /wp-signup.php - регистрация новых пользователей
# /wp-comments-post.php - комментарии (подключайте disqus.com)
# /wp-cron.php - если вы настроите настоящий крон через
# define('DISABLE_WP_CRON', true);
# php -q wp-cron.php
# /wp-content/themes/*.\.php
# /wp-admin/maint/repair.php - ???
# /wp-admin/(?:includes|network|user)/.*\.php - ???
# /wp-blog-header.php - ???
# /wp-links-opml.php - ???
# /wp-load.php - ???
# /wp-settings.php - ???
# запрещаем доступ к WP REST API v2. некоторые плагины могут перестать работать
location ~* ^/wp-json/? {
return 403;
}
# запрещаем доступ ко всем остальным скриптам
location ~* \.php$ {
return 403;
}
# запрещаем доступ ко всему что начинается с точки (.htaccess, .git/)
location ~* /\. {
return 403;
}
# запрещаем доступ к бэкапам и дампам, которые иногда бывают в папке вебсервера
location ~* \.(sql|sql\.gz)$ {
return 403;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|rar|tgz|gz|rar|bz2|doc|docx|xls|xlsx|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
limit_except GET HEAD {
deny all;
}
#access_log off;
log_not_found off;
expires max;
}
location ~* ^.+\.(css|js)$ {
limit_except GET HEAD {
deny all;
}
#access_log off;
expires 1d;
}
# если этот конфиг оказался для вас полезен - поддержите автора монеткой bitcoin:1MjxPWAyebHhjQdjLPGcV5oZd5VbEWrAga
}
# /etc/nginx/nginx.conf
#
# справку по любой директиве конфига можно получить так:
# http://nginx.org/r/variables_hash_max_size/ru
#
# лучше использовать значения по-умолчанию для большинства директив и современную версию nginx
#
# если решили что-то покрутить и потюнить, почитайте официальные рукводства и эти материалы:
# https://www.nginx.com/blog/tuning-nginx/
# https://www.nginx.com/resources/admin-guide/serving-static-content/
# Максимальное количество соединений, которые nginx
# может обслуживать одновременно определяются произведением двух параметров:
# Всего соединений = worker_processes * worker_connections
user www-data;
# количество рабочих процессов должно быть не больше количества ядер процессора
# за исключением ситуации, когда nginx делает много файловых операций. лучше оставить auto
worker_processes 2;
pid /var/run/nginx.pid;
# максимальное число открытых файлов (RLIMIT_NOFILE) для рабочих процессов
# нужно помнить, каждое новое соединение создаёт как минимум два открытых "файла"
# worker_rlimit_nofile = worker_processes * worker_connections * 2
# ниже установлено значение для двух воркеров для этого примера конфигурации
worker_rlimit_nofile 16384;
events {
# максимальное число соединений, которое одновременно может открыть рабочий процесс
worker_connections 4096;
# есть смысл включать только для синтетических тестов типа ab и siedge
# multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# if you find this config useful, please consider donating bitcoin:1MjxPWAyebHhjQdjLPGcV5oZd5VbEWrAga
# обязательно нужно включать sendfile_max_chunk, если включаем sendfile,
# иначе один посетитель с быстрым каналом заблокирует рабочий процесс
sendfile on;
sendfile_max_chunk 1m;
tcp_nopush on;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
resolver 8.8.8.8 8.8.4.4;
server_tokens off;
# увеличиваем размеры бакетов для переменных,
# если у нас множество сайтов и переменных
# при ошибке "[emerg]: could not build the variables_hash"
# variables_hash_max_size 1024;
# variables_hash_bucket_size 128;
# увеличиваем размер бакета для имён серверов,
# если их много или они длинные
# e.g. example.com www.example.com somelongsubdomain.example.com
# при ошибке "[emerg]: could not build the server_names_hash"
# server_names_hash_bucket_size 128;
# см. комментарий к large_client_header_buffers ниже. то же самое, но со стороны бэкэнда
# при ошибке "[error]: upstream sent too big header while reading response header from upstream"
fastcgi_buffers 8 16k;
fastcgi_buffer_size 16k;
# если к сайту не подключен внешний сервис защиты от DDoS, а соединений много,
# можно уменьшить таймаут между двумя операциями чтения ответа клиентом
# send_timeout 20s;
# и чтения тела запроса клиента
# client_body_timeout 20s;
# и немного сэкономить память на уже закрытых соединениях (на keep-alive не работает)
# reset_timedout_connection on;
# увеличивать client_max_body_size здесь не нужно, это нужно сделать в контексте
# того локейшна, который будет обрабатывать загружаемые пользователями файлы
# часто это локейшн с админкой сайта или вообще отдельный скрипт загрузки
# если URI с огромным QUERY_STRING, в заголовках запроса или в cookie куча инфы
# Строка запроса не должна превышать размера одного буфера,
# иначе клиенту возвращается ошибка 414 (Request-URI Too Large).
# Поле заголовка запроса также не должно превышать размера одного буфера,
# иначе клиенту возвращается ошибка 400 (Bad Request).
# large_client_header_buffers 8 16k;
# для высоконагруженных серверов с огромным количеством файлов, можно включить кеширование
# дескрипторов открытых файлов, информации об их размерах, времени модификации и иного
# open_file_cache max=300000 inactive=20s;
# open_file_cache_valid 30s;
# open_file_cache_min_uses 2;
# open_file_cache_errors on;
# при превышении рейтлимита по запросам в секунду возвращаем код 429 "Too Many Requests"
limit_req_status 429;
# включаем сжатие, причём не только для text/html
gzip on;
gzip_disable "msie6";
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/x-javascript
application/xml
application/xml+rss;
# определяем более информативный формат лога
log_format wtimes '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'"$request_time" ' # время обработки запроса в секундах с точностью до миллисекунд; время, прошедшее с момента чтения первых байт от клиента до момента записи в лог после отправки последних байт клиенту
'"$request_length" ' # длина запроса (включая строку запроса, заголовок и тело запроса)
'"$request_completion" ' # “OK” если запрос завершился, либо пустая строка
'"$host" "$server_name" ' # также записываем в лог заголовок Host клиента и server_name
'"$upstream_cache_status" '
'"$http_cf_connecting_ip" ' # оригинальный айпишник юзера, пришедшего через cloudflare
'"$server_port"';
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
# если этот конфиг оказался для вас полезен - поддержите автора монеткой bitcoin:1MjxPWAyebHhjQdjLPGcV5oZd5VbEWrAga
}
# /etc/nginx/snippet.d/sec_headers
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment