Nginx vs Apache - Konfiguracja produkcyjna serwerów WWW
Nginx Apache Konfiguracja
devopsNginx vs Apache - Konfiguracja produkcyjna serwerów WWW
Wybór serwera WWW to jedna z najważniejszych decyzji infrastrukturalnych w każdym projekcie webowym. Nginx i Apache dominują rynek od lat, ale fundamentalnie różnią się architekturą, sposobem obsługi żądań i scenariuszami, w których sprawdzają się najlepiej. W tym artykule przeprowadzimy kompleksowe porównanie obu serwerów, prezentując gotowe konfiguracje produkcyjne, optymalizacje wydajności oraz rekomendacje dotyczące tego, kiedy wybrać który serwer.
Architektura - event-driven vs process-based#
Fundamentalna różnica między Nginx i Apache leży w sposobie obsługi połączeń przychodzących. Zrozumienie tej różnicy jest kluczowe dla prawidłowej konfiguracji produkcyjnej.
Nginx - architektura event-driven#
Nginx wykorzystuje asynchroniczny, sterowany zdarzeniami model obsługi połączeń. Jeden proces worker może obsługiwać tysiące jednoczesnych połączeń dzięki nieblokującemu I/O i pętli zdarzeń (epoll na Linuxie, kqueue na BSD).
# /etc/nginx/nginx.conf - Konfiguracja główna
user www-data;
worker_processes auto; # Jeden worker na rdzeń CPU
worker_rlimit_nofile 65535; # Limit otwartych plików
pid /run/nginx.pid;
events {
worker_connections 4096; # Połączenia na worker
multi_accept on; # Akceptuj wiele połączeń jednocześnie
use epoll; # Mechanizm event na Linux
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off; # Ukryj wersję serwera
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logowanie
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
W tym modelu pojedynczy worker process z 4096 połączeniami i przy 4 rdzeniach CPU może teoretycznie obsłużyć do 16 384 jednoczesnych połączeń - bez tworzenia nowego procesu czy wątku dla każdego żądania.
Apache - architektura process/thread-based#
Apache tradycyjnie wykorzystuje model prefork (osobny proces na każde żądanie) lub worker/event MPM (połączenie procesów i wątków). W nowoczesnych instalacjach zalecany jest event MPM:
# /etc/apache2/mods-available/mpm_event.conf
<IfModule mpm_event_module>
StartServers 4
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 10000
ServerLimit 16
</IfModule>
Apache z event MPM jest znacznie wydajniejszy niż prefork, ale nadal wymaga więcej zasobów na każde połączenie niż Nginx. Każdy MaxRequestWorkers odpowiada jednemu jednocześnie obsługiwanemu żądaniu.
Nginx jako reverse proxy#
Nginx najczęściej wykorzystywany jest jako reverse proxy przed serwerami aplikacyjnymi. Oto kompletna konfiguracja produkcyjna:
# /etc/nginx/sites-available/app.conf
upstream backend_app {
least_conn; # Load balancing - najmniej połączeń
server 127.0.0.1:3000 weight=3; # Główna instancja
server 127.0.0.1:3001 weight=2; # Dodatkowa instancja
server 127.0.0.1:3002 backup; # Backup - tylko gdy główne padną
keepalive 32; # Pula połączeń keep-alive
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL - konfiguracja poniżej
include /etc/nginx/snippets/ssl-params.conf;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Rozmiar ciała żądania
client_max_body_size 64M;
client_body_buffer_size 128k;
# Proxy do backendu
location / {
proxy_pass http://backend_app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
# Timeouty proxy
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Buforowanie
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
# Pliki statyczne bezpośrednio z Nginx
location /static/ {
alias /var/www/app/static/;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location /_next/static/ {
alias /var/www/app/.next/static/;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
}
Apache - Virtual Hosts i .htaccess#
Apache oferuje potężny system konfiguracji oparty na Virtual Hosts i plikach .htaccess:
# /etc/apache2/sites-available/app.conf
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent / https://example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/app/public
# SSL
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
# Logowanie
ErrorLog ${APACHE_LOG_DIR}/app-error.log
CustomLog ${APACHE_LOG_DIR}/app-access.log combined
# Katalog główny
<Directory /var/www/app/public>
AllowOverride All
Require all granted
Options -Indexes +FollowSymLinks
</Directory>
# Proxy do Node.js / Next.js
ProxyPreserveHost On
ProxyPass /api http://127.0.0.1:3000/api
ProxyPassReverse /api http://127.0.0.1:3000/api
# Pliki statyczne
<LocationMatch "^/static/">
ExpiresActive On
ExpiresDefault "access plus 1 year"
Header set Cache-Control "public, immutable"
</LocationMatch>
# Nagłówki bezpieczeństwa
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</VirtualHost>
Plik .htaccess dla aplikacji PHP/Laravel:
# /var/www/app/public/.htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Przekierowanie na HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Usunięcie trailing slash
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]
# Front controller - Laravel
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
# Kompresja
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/css
AddOutputFilterByType DEFLATE text/javascript application/javascript
AddOutputFilterByType DEFLATE application/json application/xml
AddOutputFilterByType DEFLATE image/svg+xml
</IfModule>
# Cache przeglądarki
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType font/woff2 "access plus 1 year"
</IfModule>
SSL/TLS z Let's Encrypt#
Konfiguracja SSL jest krytyczna dla bezpieczeństwa i SEO. Oto optymalne ustawienia dla obu serwerów.
Nginx - SSL snippet#
# /etc/nginx/snippets/ssl-params.conf
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;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Sesje SSL
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# HSTS - Strict Transport Security
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Diffie-Hellman
ssl_dhparam /etc/nginx/dhparam.pem;
Generowanie parametrów DH:
openssl dhparam -out /etc/nginx/dhparam.pem 4096
Automatyczne odnawianie certyfikatów#
# Certbot z automatycznym odnowieniem
sudo certbot --nginx -d example.com -d www.example.com
# Crontab - odnowienie co 12h
0 */12 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx"
PHP-FPM - konfiguracja dla Nginx i Apache#
PHP-FPM (FastCGI Process Manager) to zalecany sposób uruchamiania PHP zarówno z Nginx, jak i Apache.
Konfiguracja puli PHP-FPM#
; /etc/php/8.3/fpm/pool.d/www.conf
[www]
user = www-data
group = www-data
; Socket UNIX - szybszy niż TCP
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
; Zarządzanie procesami
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 1000
; Slow log do debugowania
slowlog = /var/log/php-fpm/slow.log
request_slowlog_timeout = 5s
; Limity
request_terminate_timeout = 120s
php_admin_value[memory_limit] = 256M
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64M
; OPcache
php_admin_value[opcache.enable] = 1
php_admin_value[opcache.memory_consumption] = 256
php_admin_value[opcache.max_accelerated_files] = 20000
php_admin_value[opcache.validate_timestamps] = 0
Nginx z PHP-FPM#
server {
listen 443 ssl http2;
server_name laravel-app.com;
root /var/www/laravel/public;
index index.php;
include /etc/nginx/snippets/ssl-params.conf;
ssl_certificate /etc/letsencrypt/live/laravel-app.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/laravel-app.com/privkey.pem;
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;
# Buforowanie FastCGI
fastcgi_buffering on;
fastcgi_buffer_size 16k;
fastcgi_buffers 16 16k;
# Timeouty
fastcgi_connect_timeout 60;
fastcgi_send_timeout 120;
fastcgi_read_timeout 120;
}
# Blokowanie dostępu do plików ukrytych
location ~ /\.(?!well-known) {
deny all;
}
# Blokowanie dostępu do wrażliwych plików
location ~* \.(env|log|git|sql|bak)$ {
deny all;
return 404;
}
}
Apache z PHP-FPM#
<VirtualHost *:443>
ServerName laravel-app.com
DocumentRoot /var/www/laravel/public
# PHP-FPM przez proxy
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>
<Directory /var/www/laravel/public>
AllowOverride All
Require all granted
Options -Indexes
</Directory>
# Blokowanie dostępu do plików ukrytych
<DirectoryMatch "/\.">
Require all denied
</DirectoryMatch>
</VirtualHost>
Load balancing#
Nginx - zaawansowany load balancing#
upstream php_backend {
# Algorytm - ip_hash zapewnia sticky sessions
ip_hash;
server 10.0.0.10:9000 weight=5 max_fails=3 fail_timeout=30s;
server 10.0.0.11:9000 weight=3 max_fails=3 fail_timeout=30s;
server 10.0.0.12:9000 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.0.13:9000 backup;
keepalive 64;
}
upstream node_backend {
least_conn;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name app.example.com;
location /api/ {
proxy_pass http://php_backend;
proxy_next_upstream error timeout http_500 http_502 http_503;
proxy_next_upstream_tries 3;
}
location / {
proxy_pass http://node_backend;
proxy_next_upstream error timeout http_502;
}
}
Apache - mod_proxy_balancer#
<Proxy "balancer://app_cluster">
BalancerMember "http://10.0.0.10:8080" route=node1 loadfactor=5
BalancerMember "http://10.0.0.11:8080" route=node2 loadfactor=3
BalancerMember "http://10.0.0.12:8080" route=node3 loadfactor=2 status=+H
ProxySet lbmethod=byrequests stickysession=JSESSIONID
</Proxy>
<VirtualHost *:443>
ServerName app.example.com
ProxyPreserveHost On
ProxyPass / "balancer://app_cluster/"
ProxyPassReverse / "balancer://app_cluster/"
# Panel zarządzania load balancerem
<Location "/balancer-manager">
SetHandler balancer-manager
Require ip 10.0.0.0/8
</Location>
</VirtualHost>
Konfiguracja cache#
Nginx - FastCGI cache i proxy cache#
# Definicja strefy cache (w bloku http)
fastcgi_cache_path /var/cache/nginx/fastcgi
levels=1:2
keys_zone=PHPCACHE:100m
max_size=2g
inactive=60m
use_temp_path=off;
proxy_cache_path /var/cache/nginx/proxy
levels=1:2
keys_zone=PROXYCACHE:100m
max_size=5g
inactive=24h
use_temp_path=off;
server {
# FastCGI cache dla PHP
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
include fastcgi_params;
fastcgi_cache PHPCACHE;
fastcgi_cache_valid 200 301 302 60m;
fastcgi_cache_valid 404 1m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_bypass $cookie_nocache $arg_nocache;
add_header X-Cache-Status $upstream_cache_status;
}
# Proxy cache dla API
location /api/public/ {
proxy_pass http://backend_app;
proxy_cache PROXYCACHE;
proxy_cache_valid 200 10m;
proxy_cache_key "$request_uri";
add_header X-Cache-Status $upstream_cache_status;
}
}
Apache - mod_cache#
# Włączanie modułów cache
# a2enmod cache cache_disk headers
CacheQuickHandler off
CacheLock on
CacheLockPath /tmp/mod_cache-lock
CacheLockMaxAge 5
<VirtualHost *:443>
CacheEnable disk /
CacheRoot /var/cache/apache2/mod_cache_disk
CacheDefaultExpire 3600
CacheMaxExpire 86400
CacheIgnoreNoLastMod On
# Nie cache'uj panelu admina
CacheDisable /admin
CacheDisable /api/auth
# Cache headers
<LocationMatch "^/static/">
Header set Cache-Control "public, max-age=31536000, immutable"
</LocationMatch>
</VirtualHost>
Kompresja gzip i Brotli#
Nginx#
# Gzip
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/javascript
application/json
application/xml
application/xml+rss
image/svg+xml
font/woff2;
# Brotli (wymaga modułu ngx_brotli)
brotli on;
brotli_comp_level 6;
brotli_static on; # Serwuj pre-kompresowane pliki .br
brotli_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml
font/woff2;
Apache#
# mod_deflate (gzip)
<IfModule mod_deflate.c>
SetOutputFilter DEFLATE
DeflateCompressionLevel 5
AddOutputFilterByType DEFLATE text/html text/plain text/css
AddOutputFilterByType DEFLATE text/javascript application/javascript
AddOutputFilterByType DEFLATE application/json application/xml
AddOutputFilterByType DEFLATE image/svg+xml font/woff2
# Nie kompresuj już skompresowanych plików
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|webp|gz|zip|br)$ no-gzip
</IfModule>
# Brotli (wymaga mod_brotli - Apache 2.4.26+)
<IfModule mod_brotli.c>
AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/css
AddOutputFilterByType BROTLI_COMPRESS text/javascript application/javascript
AddOutputFilterByType BROTLI_COMPRESS application/json application/xml
BrotliCompressionQuality 5
</IfModule>
Nagłówki bezpieczeństwa#
Nginx - kompletna konfiguracja security headers#
# /etc/nginx/snippets/security-headers.conf
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" 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' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.example.com;" always;
# Usunięcie nagłówków ujawniających technologię
proxy_hide_header X-Powered-By;
fastcgi_hide_header X-Powered-By;
Rate limiting#
Nginx#
# Definicja stref rate limitingu (w bloku http)
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
# Ogólny rate limit dla API
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
proxy_pass http://backend_app;
}
# Ścisły rate limit dla logowania
location /api/auth/login {
limit_req zone=login burst=3 nodelay;
limit_req_status 429;
proxy_pass http://backend_app;
}
# Limit jednoczesnych połączeń
location /downloads/ {
limit_conn addr 5;
limit_rate 1m; # 1MB/s na połączenie
alias /var/www/downloads/;
}
}
Apache - mod_ratelimit i mod_evasive#
# mod_ratelimit - limit przepustowości
<Location "/downloads">
SetOutputFilter RATE_LIMIT
SetEnv rate-limit 1024 # 1024 KB/s
</Location>
# mod_evasive - ochrona przed DDoS
<IfModule mod_evasive20.c>
DOSHashTableSize 3097
DOSPageCount 5 # Max 5 żądań na stronę/s
DOSSiteCount 50 # Max 50 żądań na stronę/s
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 60 # Blokada na 60s
DOSEmailNotify admin@example.com
</IfModule>
WebSocket proxying#
Nginx#
# Mapa do obsługi WebSocket upgrade
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
location /ws/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Timeout dla WebSocket (dłuższy niż standardowy)
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
Apache#
# Wymagane moduły: mod_proxy_wstunnel
# a2enmod proxy_wstunnel
<VirtualHost *:443>
# WebSocket proxy
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /ws/(.*) ws://127.0.0.1:3000/ws/$1 [P,L]
ProxyPass /ws/ ws://127.0.0.1:3000/ws/
ProxyPassReverse /ws/ ws://127.0.0.1:3000/ws/
# Timeout dla WebSocket
ProxyTimeout 3600
</VirtualHost>
Porównanie wydajności#
Poniżej prezentujemy typowe wyniki benchmarków dla obu serwerów w różnych scenariuszach:
| Metryka | Nginx | Apache (event MPM) | |---------|-------|---------------------| | Pliki statyczne (req/s) | ~25 000 | ~10 000 | | Reverse proxy (req/s) | ~18 000 | ~8 000 | | PHP-FPM (req/s) | ~3 500 | ~3 200 | | Zużycie RAM (1000 conn.) | ~50 MB | ~200 MB | | Zużycie RAM (10000 conn.) | ~80 MB | ~1.5 GB | | Czas odpowiedzi p99 | ~2 ms | ~8 ms | | Max. jednoczesnych połączeń | ~100 000+ | ~10 000 |
Nginx dominuje w scenariuszach z wieloma jednocznymi połączeniami i serwowaniem plików statycznych. Przy dynamicznych żądaniach PHP-FPM różnica jest mniejsza, ponieważ wąskim gardłem staje się sam PHP.
Nginx dla Next.js / Node.js#
# Produkcyjna konfiguracja Nginx dla Next.js
upstream nextjs {
server 127.0.0.1:3000;
keepalive 64;
}
server {
listen 443 ssl http2;
server_name app.example.com;
include /etc/nginx/snippets/ssl-params.conf;
include /etc/nginx/snippets/security-headers.conf;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
# Next.js static assets - agresywne cache
location /_next/static/ {
proxy_pass http://nextjs;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Next.js image optimization
location /_next/image {
proxy_pass http://nextjs;
proxy_cache PROXYCACHE;
proxy_cache_valid 200 24h;
proxy_cache_key "$request_uri";
}
# Publiczne assety
location /public/ {
alias /var/www/app/public/;
expires 30d;
add_header Cache-Control "public";
access_log off;
}
# Wszystko inne do Next.js
location / {
proxy_pass http://nextjs;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
}
}
Apache dla PHP / Laravel#
# Produkcyjna konfiguracja Apache dla Laravel
<VirtualHost *:443>
ServerName laravel.example.com
DocumentRoot /var/www/laravel/public
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/laravel.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/laravel.example.com/privkey.pem
# PHP-FPM
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
</FilesMatch>
# Katalog główny
<Directory /var/www/laravel/public>
AllowOverride All
Require all granted
Options -Indexes +FollowSymLinks -MultiViews
</Directory>
# Blokowanie dostępu do wrażliwych katalogów
<DirectoryMatch "^/var/www/laravel/(storage|vendor|bootstrap/cache)">
Require all denied
</DirectoryMatch>
# Blokowanie plików .env
<FilesMatch "^\.env">
Require all denied
</FilesMatch>
# Nagłówki bezpieczeństwa
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
# Kompresja
SetOutputFilter DEFLATE
AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json
# Logowanie
ErrorLog ${APACHE_LOG_DIR}/laravel-error.log
CustomLog ${APACHE_LOG_DIR}/laravel-access.log combined
</VirtualHost>
Kiedy wybrać Nginx, a kiedy Apache?#
Wybierz Nginx gdy:#
- Budujesz aplikację Node.js / Next.js - Nginx jest naturalnym reverse proxy dla aplikacji Node
- Potrzebujesz obsługi wielu jednoczesnych połączeń - WebSocket, SSE, long polling
- Serwujesz dużo plików statycznych - Nginx jest znacznie wydajniejszy
- Load balancing - wbudowane algorytmy load balancingu są elastyczne i wydajne
- Mikroserwisy - jako API gateway z routingiem do wielu backendów
- Ograniczone zasoby - mniejsze zużycie RAM i CPU
- Konteneryzacja (Docker/K8s) - mniejszy footprint, szybszy start
Wybierz Apache gdy:#
- Hosting współdzielony -
.htaccesspozwala na konfigurację per-katalog bez restartu serwera - Aplikacje PHP/Laravel - natywna integracja z mod_php (choć PHP-FPM jest zalecany)
- Potrzebujesz dynamicznej konfiguracji -
.htaccessumożliwia zmiany bez restartu - Złożone reguły rewrite - mod_rewrite jest bardziej rozbudowany
- Wiele stron na jednym serwerze - izolacja konfiguracji przez
.htaccess - Istniejąca infrastruktura - migracja z Apache na Nginx nie zawsze jest opłacalna
Rozwiązanie hybrydowe#
W wielu produkcyjnych środowiskach najlepszym rozwiązaniem jest kombinacja obu serwerów:
Klient → Nginx (reverse proxy, SSL, cache, static files)
├── Node.js / Next.js (port 3000)
├── Apache + PHP-FPM (port 8080)
└── Inne mikroserwisy
Nginx pełni rolę front-end proxy, obsługując SSL termination, kompresję, cache i pliki statyczne, podczas gdy Apache obsługuje aplikacje PHP z pełną mocą .htaccess i mod_rewrite.
Podsumowanie#
Zarówno Nginx, jak i Apache są dojrzałymi, niezawodnymi serwerami WWW. Nginx dominuje w scenariuszach wymagających wysokiej wydajności, dużej liczby połączeń i pracy jako reverse proxy. Apache oferuje większą elastyczność konfiguracji i jest tradycyjnym wyborem dla aplikacji PHP. W nowoczesnych architekturach, Nginx jako front-end proxy z Apache jako backend dla PHP stanowi sprawdzone rozwiązanie łączące zalety obu serwerów.
Potrzebujesz profesjonalnej konfiguracji serwerów WWW? Zespół MDS Software Solutions Group specjalizuje się w optymalizacji infrastruktury webowej. Od konfiguracji Nginx i Apache, przez wdrożenia CI/CD, po skalowanie aplikacji w chmurze - pomożemy Ci zbudować wydajną i bezpieczną infrastrukturę. Skontaktuj się z nami, aby omówić Twój projekt.
Zespół ekspertów programistycznych specjalizujących się w nowoczesnych technologiach webowych.