From 3d99587cb03a85ac609969b8638714a969842993 Mon Sep 17 00:00:00 2001 From: dcs-soni <52232900+dcs-soni@users.noreply.github.com> Date: Mon, 10 Nov 2025 09:51:10 +0530 Subject: [PATCH] feat: add https server block when ssl certs are mounted on ec2 --- apps/web/nginx.conf | 136 +++++++++++++++++++++++++++++++++----------- 1 file changed, 104 insertions(+), 32 deletions(-) diff --git a/apps/web/nginx.conf b/apps/web/nginx.conf index 0213a90..4da60b9 100644 --- a/apps/web/nginx.conf +++ b/apps/web/nginx.conf @@ -1,30 +1,15 @@ -# Server block - defines how to handle requests +# HTTP Server - Works without SSL certificates server { listen 80; + listen [::]:80; - # ( _ for default/catch-all) server_name _; - # Root directory where static files are located - # This is where we copied our React build output root /usr/share/nginx/html; - - # Default file to serve index index.html; location / { - # try_files directive - tries files in order: - # 1. $uri - Try the exact URI (e.g., /logo.png) - # 2. $uri/ - Try as a directory with index.html - # 3. /index.html - Fallback to index.html for all other routes - # - # This is CRITICAL for React Router: - # - /dashboard → index.html (React Router handles it) - # - /projects/123 → index.html (React Router handles it) - # - /static/logo.png → serves the actual file try_files $uri $uri/ /index.html; - - # Add headers for browser caching add_header Cache-Control "no-cache, must-revalidate"; } @@ -32,14 +17,10 @@ server { location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; - - # Allow files to be served from this location try_files $uri =404; } - - # This file is generated at container startup - # Contains runtime environment variables + # Runtime environment variables location = /env-config.js { add_header Cache-Control "no-store, no-cache, must-revalidate"; try_files $uri =404; @@ -68,20 +49,12 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } - # Security Headers - - # Prevent clickjacking attacks add_header X-Frame-Options "SAMEORIGIN" always; - - # Prevent MIME type sniffing add_header X-Content-Type-Options "nosniff" always; - - # Enable XSS protection add_header X-XSS-Protection "1; mode=block" always; # Compression - # Enable gzip compression for better performance gzip on; gzip_vary on; gzip_min_length 1024; @@ -100,8 +73,108 @@ server { image/svg+xml; # Error Pages + error_page 404 /index.html; + + # Don't log access to favicon (reduces log noise) + location = /favicon.ico { + log_not_found off; + access_log off; + } + + location = /robots.txt { + log_not_found off; + access_log off; + } +} + +# HTTPS Server - Only active if SSL certificates are mounted +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + + server_name _; - # Custom error page (could create 404.html in build) + # SSL Configuration + # These paths should match where certificates are mounted in docker-compose + ssl_certificate /etc/nginx/ssl/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/privkey.pem; + + # SSL Settings + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + add_header Cache-Control "no-cache, must-revalidate"; + } + + # Cache static assets for 1 year (they have content hashes) + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + # Runtime environment variables + location = /env-config.js { + add_header Cache-Control "no-store, no-cache, must-revalidate"; + try_files $uri =404; + } + + # API Proxy - Routes API requests to backend + location /api { + proxy_pass http://backend:3002; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + 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; + } + + # Health check endpoint proxy + location /health { + proxy_pass http://backend:3002/health; + 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; + } + + # Security Headers (with HSTS for HTTPS) + 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 Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + # Compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/rss+xml + font/truetype + font/opentype + application/vnd.ms-fontobject + image/svg+xml; + + # Error Pages error_page 404 /index.html; # Don't log access to favicon (reduces log noise) @@ -110,7 +183,6 @@ server { access_log off; } - # Don't log access to robots.txt location = /robots.txt { log_not_found off; access_log off;