Загрузка...
Загрузка...
Полное руководство по настройке Nginx. Виртуальные хосты, SSL, проксирование, кэширование, безопасность. Практические примеры конфигов для разных проектов.
Полное руководство по .htaccess для Apache. Редиректы, rewrite правила, безопасность, кэширование, сжатие. Практические примеры для веб-разработчиков и SEO.
DevOpsПолная настройка SSL/TLS в Nginx: установка Certbot, server block, HSTS, OCSP stapling, cipher suites, редирект HTTP на HTTPS.
БезопасностьПолное руководство по настройке Brotli сжатия на веб-серверах: Nginx, Apache, сравнение с Gzip, уровни сжатия и проверка работы.
БезопасностьПошаговая настройка HSTS для принудительного HTTPS. max-age, includeSubDomains, preload. Nginx, Apache, Cloudflare. Проверка и отладка.
Поделитесь с коллегами или изучите другие материалы блога
Nginx обслуживает более 30% всех сайтов в интернете. Высокая производительность, предсказуемое потребление памяти и способность обрабатывать десятки тысяч одновременных соединений делают его стандартом де-факто для серьёзных веб-проектов. Однако между «установить Nginx» и «настроить Nginx правильно» лежит пропасть, в которую проваливаются даже опытные разработчики.
Эта статья — практическое руководство от базовой структуры конфигурации до продвинутых техник кэширования и защиты от DDoS. Если нужно быстро собрать конфиг под конкретную задачу, воспользуйтесь Генератором Nginx конфигурации на reChecker — он создаст оптимальную конфигурацию за несколько кликов.
Конфигурация построена на иерархии контекстов — вложенных блоков, каждый из которых отвечает за свою область. Главный файл расположен по пути /etc/nginx/nginx.conf:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;
events {
worker_connections 1024;
multi_accept on;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Директива worker_processes auto создаёт по одному рабочему процессу на ядро CPU. Параметр worker_connections определяет максимум одновременных соединений на процесс, итого пропускная способность — worker_processes × worker_connections. Директива use epoll задаёт механизм мультиплексирования Linux (на FreeBSD — kqueue).
Директивы в родительском контексте наследуются дочерними и могут быть переопределены. Конфигурации сайтов выносятся в /etc/nginx/sites-available/ с символическими ссылками из /etc/nginx/sites-enabled/:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo nginx -s reload
Всегда выполняйте nginx -t перед перезагрузкой — синтаксическая ошибка может привести к остановке сервера.
Server block — аналог виртуального хоста Apache. Минимальная конфигурация:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/public;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Параметр server_name принимает точные имена, маски и регулярные выражения. Приоритет: точное имя → маска в начале (*.example.com) → маска в конце (example.*) → регулярное выражение. Если ни один блок не совпал, запрос идёт в дефолтный сервер:
server {
listen 80 default_server;
server_name _;
return 444;
}
Код 444 — специфичный для Nginx ответ, который разрывает соединение без отправки заголовков. Полезно для блокировки запросов по IP-адресу.
Nginx отдаёт статические файлы значительно быстрее любого application-сервера:
location ~* \.(jpg|jpeg|png|gif|ico|webp|avif|svg)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.(css|js)$ {
expires 7d;
add_header Cache-Control "public";
access_log off;
}
location ~* \.(woff2?|ttf|eot|otf)$ {
expires 365d;
add_header Cache-Control "public, immutable";
add_header Access-Control-Allow-Origin "*";
access_log off;
}
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
Параметр immutable в Cache-Control сообщает браузеру, что файл гарантированно не изменится — полностью исключаются повторные обращения к серверу. Разница между root и alias: первый добавляет путь location к корню, второй заменяет location целиком.
Для SPA-приложений необходим fallback:
location / {
try_files $uri $uri/ /index.html;
}
upstream node_backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
keepalive 32;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://node_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Директива keepalive 32 поддерживает пул постоянных соединений к бэкенду. Заголовки Upgrade и Connection необходимы для WebSocket.
server {
listen 80;
server_name php-app.example.com;
root /var/www/php-app/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300s;
}
location ~* /uploads/.*\.php$ {
deny all;
}
}
Unix-сокет вместо TCP даёт прирост производительности 5–10% за счёт отсутствия overhead TCP-стека.
upstream gunicorn_backend {
server unix:/run/gunicorn/app.sock fail_timeout=0;
}
server {
listen 80;
server_name python-app.example.com;
client_max_body_size 10M;
location / {
proxy_pass http://gunicorn_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /static/ {
alias /var/www/python-app/staticfiles/;
expires 30d;
}
}
Для балансировки между бэкендами Nginx поддерживает round robin (по умолчанию), weighted round robin, least_conn (наименее загруженный) и ip_hash (привязка клиента к серверу).
Для проверки текущего сертификата используйте SSL Checker на reChecker.
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_dhparam /etc/nginx/dhparam.pem;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}
OCSP Stapling устраняет необходимость для браузера самостоятельно проверять статус отзыва сертификата, ускоряя TLS-handshake на 100–300 мс. Вынесите общие SSL-параметры в /etc/nginx/snippets/ssl-params.conf и подключайте через include — не дублируйте в каждом server-блоке.
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 301 https://example.com$request_uri;
}
Оба варианта (www и non-www) должны иметь валидный SSL-сертификат — браузер проверяет его до получения инструкции перенаправления.
map $request_uri $redirect_url {
/old-page-1 /new-page-1;
/products/123 /catalog/item-123;
/about-us /company;
}
server {
if ($redirect_url) {
return 301 $redirect_url;
}
}
Директива map обрабатывается на этапе инициализации и не влияет на производительность даже при тысячах записей.
proxy_cache_path /var/cache/nginx/proxy
levels=1:2
keys_zone=proxy_cache:10m
max_size=1g
inactive=60m
use_temp_path=off;
server {
location / {
proxy_pass http://backend;
proxy_cache proxy_cache;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 30s;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
proxy_cache_key "$scheme$request_method$host$request_uri";
add_header X-Cache-Status $upstream_cache_status;
}
location /api/ {
proxy_pass http://backend;
proxy_no_cache 1;
proxy_cache_bypass 1;
}
}
proxy_cache_use_stale при ошибке бэкенда отдаёт устаревшую версию вместо 502. Директива proxy_cache_lock on предотвращает thundering herd — только один запрос к каждому URL уходит на бэкенд, остальные ждут кэша.
fastcgi_cache_path /var/cache/nginx/fastcgi
levels=1:2 keys_zone=fcgi_cache:10m
max_size=512m inactive=30m;
map $request_uri $skip_cache {
default 0;
~*/wp-admin 1;
~*/cart 1;
~*/checkout 1;
}
server {
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_cache fcgi_cache;
fastcgi_cache_valid 200 10m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
}
}
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types text/plain text/css text/javascript
application/json application/javascript
application/xml image/svg+xml;
Уровень 5 — оптимальный баланс: 6–9 дают минимальный прирост при заметном росте нагрузки на CPU. Директива gzip_vary on добавляет Vary: Accept-Encoding для корректной работы с CDN.
Brotli обеспечивает на 15–25% лучшее сжатие по сравнению с Gzip:
brotli on;
brotli_comp_level 6;
brotli_min_length 256;
brotli_static on;
brotli_types text/plain text/css text/javascript
application/json application/javascript
application/xml image/svg+xml;
Директива brotli_static on отдаёт заранее сжатые .br-файлы — максимальное сжатие без нагрузки на CPU в рантайме.
Для проверки заголовков безопасности используйте инструмент Security Headers.
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
limit_req_status 429;
server {
limit_req zone=general burst=20 nodelay;
location /login {
limit_req zone=login burst=3 nodelay;
}
}
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;" always;
client_max_body_size 10m;
client_body_timeout 12s;
client_header_timeout 12s;
map $http_user_agent $bad_bot {
default 0;
~*sqlmap 1;
~*nikto 1;
~*scanner 1;
}
server {
if ($bad_bot) { return 403; }
}
location ~ /\.git { deny all; return 404; }
location ~* \.(env|ini|log|conf|bak|sql)$ { deny all; return 404; }
log_format detailed '$remote_addr [$time_local] "$request" '
'$status $body_bytes_sent rt=$request_time '
'urt=$upstream_response_time cs=$upstream_cache_status';
log_format json escape=json '{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status":$status,'
'"request_time":$request_time,'
'"upstream_response_time":"$upstream_response_time"'
'}';
JSON-формат удобен для интеграции с ELK Stack и Loki + Grafana.
map $http_user_agent $loggable {
default 1;
~*monitoring 0;
~*UptimeRobot 0;
}
access_log /var/log/nginx/access.log detailed if=$loggable;
server {
listen 127.0.0.1:8080;
location /nginx_status {
stub_status on;
allow 127.0.0.1;
deny all;
}
}
Endpoint возвращает метрики: активные соединения, число запросов, соединения в состоянии чтения/записи/ожидания. Подключается к Prometheus через nginx-prometheus-exporter.
Самая частая ошибка — непонимание приоритетов. Nginx выбирает location в таком порядке:
= /exact — точное совпадение (наивысший приоритет)^~ /prefix — префикс с остановкой поиска regex~ \.php$ — регулярное выражение (с учётом регистра)~* \.jpg$ — регулярное выражение (без учёта регистра)/prefix — обычный префикс (наиболее длинный)sudo nginx -t # Проверка синтаксиса
sudo nginx -T # Полная конфигурация
sudo tail -50 /var/log/nginx/error.log
sudo ss -tlnp | grep nginx # Проверка портов
502 Bad Gateway — бэкенд недоступен. Проверьте, запущен ли процесс (systemctl status php8.3-fpm) и совпадают ли права на Unix-сокет.
413 Request Entity Too Large — превышен client_max_body_size, установите нужное значение в server или location.
Бесконечный цикл редиректов — конфликт между Nginx и приложением. Решение: редирект HTTP→HTTPS в отдельном server-блоке, а в основном блоке передавайте X-Forwarded-Proto:
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
proxy_set_header X-Forwarded-Proto $scheme;
location / { proxy_pass http://backend; }
}
Permission denied — Nginx работает от пользователя www-data, который должен иметь права на файлы сайта:
sudo chown -R www-data:www-data /var/www/site
sudo find /var/www/site -type d -exec chmod 755 {} \;
sudo find /var/www/site -type f -exec chmod 644 {} \;
curl -I -H "Host: example.com" http://127.0.0.1
curl -I http://example.com
curl -H "Accept-Encoding: gzip" -I https://example.com
Конфигурация Nginx — непрерывный процесс. По мере роста нагрузки добавляются правила кэширования, ужесточаются политики безопасности, тонко настраиваются параметры проксирования.
Ключевые принципы:
nginx -t перед применением.include для выделения повторяющихся блоков в отдельные файлы.Для быстрого старта используйте Генератор Nginx конфигурации на reChecker — он создаст конфигурацию с учётом SSL, сжатия и заголовков безопасности. Проверяйте результат через SSL Checker и Security Headers, а для настройки Apache — генератор .htaccess.