# Nginx configuration for Post-ERG thesis website # Place this in /etc/nginx/sites-available/posterg # Then symlink: ln -s /etc/nginx/sites-available/posterg /etc/nginx/sites-enabled/ # Rate limiting zones limit_req_zone $binary_remote_addr zone=general:10m rate=30r/m; limit_req_zone $binary_remote_addr zone=search:10m rate=30r/m; limit_req_zone $binary_remote_addr zone=admin:10m rate=10r/m; # Server block - HTTP (redirect to HTTPS in production) server { listen 80; listen [::]:80; server_name posterg.erg.be www.posterg.erg.be; # Redirect all HTTP to HTTPS (uncomment in production) # return 301 https://$server_name$request_uri; # For development/testing, allow HTTP root /var/www/html; index index.php index.html; # Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; # Disable server tokens server_tokens off; # Max upload size (for thesis files) client_max_body_size 100M; client_body_timeout 120s; # Logging access_log /var/log/nginx/posterg_access.log; error_log /var/log/nginx/posterg_error.log warn; # Block common attack patterns location ~ /\. { deny all; access_log off; log_not_found off; } location ~ \.(git|env|db-journal)$ { deny all; access_log off; log_not_found off; } # Deny access to sensitive files location ~* \.(md|txt|sql|sh|json)$ { deny all; } # Deny access to database files location ~* \.db$ { deny all; } # Deny access to shared/ directory (PHP includes only) location /shared/ { deny all; } # Deny access to tests directory location /tests/ { deny all; } # Deny access to cache directory location /cache/ { deny all; } # Admin panel - password protected location /formulaire/ { alias /var/www/html/formulaire/; # HTTP Basic Authentication auth_basic "Admin Access - Post-ERG"; auth_basic_user_file /etc/nginx/.htpasswd-posterg; # Rate limiting for admin limit_req zone=admin burst=5 nodelay; # PHP handling location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $request_filename; } # Additional security for admin add_header X-Robots-Tag "noindex, nofollow" always; } # Search endpoint - rate limiting location /search.php { limit_req zone=search burst=10 nodelay; include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; } # Public PHP files location ~ \.php$ { limit_req zone=general burst=20 nodelay; include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; # Security parameters fastcgi_param PHP_VALUE "upload_max_filesize=50M \n post_max_size=100M"; fastcgi_param PHP_ADMIN_VALUE "open_basedir=/var/www/html:/tmp"; # Timeouts fastcgi_read_timeout 120; fastcgi_send_timeout 120; } # Static files caching location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|otf)$ { expires 30d; add_header Cache-Control "public, immutable"; access_log off; } # Root location location / { try_files $uri $uri/ =404; } # Deny access to specific file types in data directories location ~* /data/.*\.(php|sh|py)$ { deny all; } } # Server block - HTTPS (production) server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name posterg.erg.be www.posterg.erg.be; root /var/www/html; index index.php index.html; # SSL certificates (Let's Encrypt) # Run: certbot --nginx -d posterg.erg.be -d www.posterg.erg.be ssl_certificate /etc/letsencrypt/live/posterg.erg.be/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/posterg.erg.be/privkey.pem; # SSL configuration 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_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_stapling on; ssl_stapling_verify on; # Security headers (HTTPS) add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; # Disable server tokens server_tokens off; # Max upload size client_max_body_size 100M; client_body_timeout 120s; # Logging access_log /var/log/nginx/posterg_ssl_access.log; error_log /var/log/nginx/posterg_ssl_error.log warn; # Block common attack patterns location ~ /\. { deny all; access_log off; log_not_found off; } location ~ \.(git|env|db-journal)$ { deny all; access_log off; log_not_found off; } # Deny access to sensitive files location ~* \.(md|txt|sql|sh|json)$ { deny all; } # Deny access to database files location ~* \.db$ { deny all; } # Deny access to shared/ directory location /shared/ { deny all; } # Deny access to tests directory location /tests/ { deny all; } # Deny access to cache directory location /cache/ { deny all; } # Admin panel - password protected location /formulaire/ { alias /var/www/html/formulaire/; # HTTP Basic Authentication auth_basic "Admin Access - Post-ERG"; auth_basic_user_file /etc/nginx/.htpasswd-posterg; # Rate limiting limit_req zone=admin burst=5 nodelay; # PHP handling location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $request_filename; } # Security headers add_header X-Robots-Tag "noindex, nofollow" always; } # Search endpoint - rate limiting location /search.php { limit_req zone=search burst=10 nodelay; include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; } # Public PHP files location ~ \.php$ { limit_req zone=general burst=20 nodelay; include snippets/fastcgi-php.conf; fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; # Security parameters fastcgi_param PHP_VALUE "upload_max_filesize=50M \n post_max_size=100M"; fastcgi_param PHP_ADMIN_VALUE "open_basedir=/var/www/html:/tmp"; # Timeouts fastcgi_read_timeout 120; fastcgi_send_timeout 120; } # Static files caching location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|otf)$ { expires 30d; add_header Cache-Control "public, immutable"; access_log off; } # Root location location / { try_files $uri $uri/ =404; } # Deny access to script files in data directories location ~* /data/.*\.(php|sh|py)$ { deny all; } }