Enhance Docker and Nginx configuration for dynamic subfolder support and SPA routing

This commit is contained in:
Tudor Stanciu 2025-09-28 04:03:24 +03:00
parent e267e58412
commit 7fa660eb71
3 changed files with 169 additions and 6 deletions

View File

@ -28,8 +28,12 @@ FROM nginx:1.29.1-alpine
# Remove default nginx website # Remove default nginx website
RUN rm -rf /usr/share/nginx/html/* RUN rm -rf /usr/share/nginx/html/*
# Create subfolder structure dynamically based on VITE_BASE_PATH
ARG VITE_BASE_PATH=""
RUN if [ -n "$VITE_BASE_PATH" ]; then mkdir -p /usr/share/nginx/html/$VITE_BASE_PATH; fi
# Copy built application # Copy built application
COPY --from=builder /app/build /usr/share/nginx/html COPY --from=builder /app/build /usr/share/nginx/html/${VITE_BASE_PATH}
# Copy nginx configuration # Copy nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf COPY nginx.conf /etc/nginx/nginx.conf
@ -44,7 +48,7 @@ EXPOSE 80
# Health check # Health check
HEALTHCHECK --interval=120s --timeout=10s --start-period=60s --retries=3 \ HEALTHCHECK --interval=120s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost/ || exit 1 CMD if [ -n "$VITE_BASE_PATH" ]; then curl -f http://localhost/$VITE_BASE_PATH/ || exit 1; else curl -f http://localhost/ || exit 1; fi
# Start nginx # Start nginx
CMD ["nginx", "-g", "daemon off;"] CMD ["nginx", "-g", "daemon off;"]

153
generate-nginx-config.sh Normal file
View File

@ -0,0 +1,153 @@
#!/bin/sh
# Generate nginx.conf based on VITE_BASE_PATH environment variable
BASE_PATH=${VITE_BASE_PATH:-""}
if [ -n "$BASE_PATH" ]; then
# Configuration for subfolder deployment
cat > /etc/nginx/nginx.conf << EOF
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
log_format main '\$remote_addr - \$remote_user [\$time_local] "\$request" '
'\$status \$body_bytes_sent "\$http_referer" '
'"\$http_user_agent" "\$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
# Static assets caching for $BASE_PATH subfolder
location ~* ^/$BASE_PATH/.*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)\$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Main application at /$BASE_PATH/ - SPA fallback
location /$BASE_PATH/ {
try_files \$uri \$uri/ /$BASE_PATH/index.html;
}
# Redirect root to application
location = / {
return 301 /$BASE_PATH/;
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
}
EOF
else
# Configuration for root deployment
cat > /etc/nginx/nginx.conf << EOF
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
log_format main '\$remote_addr - \$remote_user [\$time_local] "\$request" '
'\$status \$body_bytes_sent "\$http_referer" '
'"\$http_user_agent" "\$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
# Static assets caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)\$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Main application - SPA fallback
location / {
try_files \$uri \$uri/ /index.html;
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
}
EOF
fi
echo "Generated nginx config for BASE_PATH: '$BASE_PATH'"

View File

@ -45,15 +45,21 @@ http {
add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
# Static assets caching # Static assets caching - works for any subfolder or root
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y; expires 1y;
add_header Cache-Control "public, immutable"; add_header Cache-Control "public, immutable";
} }
# Main application - SPA fallback # SPA fallback - catch all routes and serve appropriate index.html
location / { location / {
try_files $uri $uri/ /index.html; try_files $uri $uri/ @fallback;
}
# Fallback handler for SPA routes
location @fallback {
# Try to find index.html in the same directory structure
try_files $uri/index.html /index.html;
} }
# Health check endpoint # Health check endpoint