Nginx — hardening i konfiguracja bezpieczeństwa
Opublikowano: 9 kwietnia 2026 · Kategoria: Serwer VPS
Świeżo zainstalowany Nginx jest bezpieczny w podstawach, ale daleki od wytrzymałości produkcyjnej — ujawnia wersję serwera, nie wysyła security headers i nie ma limitów żądań. Ten przewodnik pokazuje pełny hardening: od ukrycia wersji po HSTS i blokowanie złych botów, z gotowymi blokami konfiguracji do skopiowania.
Krok 1: Ukrycie wersji serwera
Domyślny Nginx ujawnia swoją wersję w nagłówkach HTTP i stronach błędów. Wyłącz to globalnie
w /etc/nginx/nginx.conf:
http {
# Ukryj wersję Nginx w nagłówkach i stronach błędów
server_tokens off;
# Opcjonalnie: całkowite ukrycie "nginx" (wymaga modułu headers-more)
# more_set_headers "Server: webserver";
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
} Krok 2: Security Headers
Dodaj security headers do bloku server każdej wirtualnej domeny lub globalnie w nginx.conf.
Kompletna konfiguracja:
server {
listen 443 ssl;
server_name twojadomena.pl;
# Zapobiega osadzaniu strony w ifranie (clickjacking)
add_header X-Frame-Options "SAMEORIGIN" always;
# Blokuje MIME-type sniffing w IE/Chrome
add_header X-Content-Type-Options "nosniff" always;
# Wymusza HTTPS przez 1 rok, rozszerza na subdomeny
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Polityka referera — nie ujawniaj URL po kliknięciu linku zewnętrznego
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Ograniczenie uprawnień przeglądarki (geolokacja, kamera, mikrofon)
add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always;
# Content Security Policy — dostosuj do swoich zasobów
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none';" always;
} | Nagłówek | Co chroni | Priorytet |
|---|---|---|
X-Frame-Options | Clickjacking przez iframe | Wysoki |
X-Content-Type-Options | MIME sniffing attacks | Wysoki |
Strict-Transport-Security | SSL stripping, downgrade attacks | Wysoki |
Referrer-Policy | Wyciek URL do zewnętrznych serwisów | Średni |
Content-Security-Policy | XSS, code injection | Wysoki (trudne we wdrożeniu) |
Permissions-Policy | Dostęp do API przeglądarki | Niski/Średni |
Krok 3: SSL/TLS Hardening
Domyślna konfiguracja SSL często akceptuje przestarzałe protokoły (TLS 1.0, 1.1) i słabe szyfry. Zaostrz konfigurację:
server {
listen 443 ssl http2;
# Ścieżki certyfikatów (Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/twojadomena.pl/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/twojadomena.pl/privkey.pem;
# Tylko TLS 1.2 i 1.3 (TLS 1.0 i 1.1 są przestarzałe)
ssl_protocols TLSv1.2 TLSv1.3;
# Mocne szyfry — preferuj szyfry serwera
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
# Sesja SSL — cache i czas życia
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP Stapling — szybsza weryfikacja certyfikatu przez przeglądarkę
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/twojadomena.pl/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Diffie-Hellman parameters (wygeneruj raz: openssl dhparam -out /etc/nginx/dhparam.pem 2048)
ssl_dhparam /etc/nginx/dhparam.pem;
} Krok 4: Rate Limiting
Ogranicz liczbę żądań per IP — chroni przed brute force i prostymi atakami DDoS:
http {
# Definicja stref rate limiting
# 10m = 10 MB pamięci (ok. 160 000 IP), 10r/s = 10 żądań/sekundę per IP
limit_req_zone $binary_remote_addr zone=global:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
# Odpowiedź przy przekroczeniu limitu
limit_req_status 429;
}
server {
# Globalny limit — wszystkie requesty
limit_req zone=global burst=20 nodelay;
# Ostrzejszy limit dla formularza logowania (WordPress, phpMyAdmin)
location = /wp-login.php {
limit_req zone=login burst=5 nodelay;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# Limit dla API REST
location /wp-json/ {
limit_req zone=global burst=30 nodelay;
}
} Krok 5: Blokowanie złych botów i wrażliwych lokalizacji
server {
# Blokuj dostęp do plików konfiguracyjnych
location ~ /\.(git|env|htaccess|htpasswd|svn|DS_Store) {
deny all;
return 404;
}
# Blokuj dostęp do plików z kopiami zapasowymi i logami
location ~* \.(bak|conf|log|sql|tar|gz|zip|old)$ {
deny all;
return 404;
}
# Blokuj złe user-agenty (boty, skanery)
if ($http_user_agent ~* (masscan|nikto|sqlmap|nmap|zgrab|dirbuster|havij|acunetix)) {
return 403;
}
# Blokuj puste user-agenty
if ($http_user_agent = "") {
return 403;
}
# Blokuj metody HTTP inne niż GET, HEAD, POST
if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|OPTIONS)$) {
return 405;
}
# Ogranicz rozmiar żądania (ochrona przed atakami przez duże body)
client_max_body_size 10M;
} Weryfikacja konfiguracji
# Sprawdź składnię konfiguracji przed przeładowaniem nginx -t # Przeładuj (bez przerwy w działaniu) systemctl reload nginx # Sprawdź nagłówki bezpieczeństwa curl -I https://twojadomena.pl # Test SSL online # https://www.ssllabs.com/ssltest/ — celuj w ocenę A+ # https://securityheaders.com/ — sprawdź wszystkie security headers
Po wdrożeniu sprawdź ocenę na SSL Labs (cel: A+) i SecurityHeaders.com (cel: A). Pełne A+ wymaga między innymi HSTS z preload i wyłączonych TLS 1.0/1.1. ModSecurity jako WAF to kolejny krok — rozważ go jeśli obsługujesz aplikacje webowe z danymi użytkowników.