# Nginx try_files in Modern Microservices with Vue 3 ## Table of Contents 1. [Introduction](#Introduction) 2. [Understanding try_files](#Understanding-try_files) 3. [Vue 3 SPA Challenges](#Vue-3-SPA-Challenges) 4. [Microservices Architecture](#Microservices-Architecture) 5. [Basic Configuration](#Basic-Configuration) 6. [Advanced Patterns](#Advanced-Patterns) 7. [Real-world Examples](#Real-world-Examples) 8. [Best Practices](#Best-Practices) 9. [Troubleshooting](#Troubleshooting) ## Introduction In modern web development, Single Page Applications (SPAs) like Vue 3 apps are often deployed in microservices architectures. This creates unique challenges for web servers like Nginx, particularly around routing and file serving. The `try_files` directive is a powerful tool that helps solve these challenges elegantly. ## Understanding try_files ### What is try_files? The `try_files` directive in Nginx attempts to serve files in a specified order. It's essential for SPAs because client-side routing needs to fall back to the main application file when direct file access fails. ### Basic Syntax ```nginx try_files $uri $uri/ @fallback; ``` **Parameters:** - `$uri` - The exact URI requested - `$uri/` - The URI with a trailing slash (for directories) - `@fallback` - A named location block for final fallback ## Vue 3 SPA Challenges ### Client-Side Routing Problem Vue 3 applications use client-side routing (Vue Router), which means: 1. **Initial Load**: `example.com/` loads `index.html` 2. **Navigation**: `example.com/dashboard` should also load `index.html` 3. **Direct Access**: Typing `example.com/dashboard` in browser fails without proper server configuration ### The 404 Problem Without proper configuration: ``` GET /dashboard → 404 Not Found ``` With `try_files`: ``` GET /dashboard → try_files → serve index.html → Vue Router handles /dashboard ``` ## Microservices Architecture ### Typical Setup ``` ┌───────────────┐ ┌──────────────┐ ┌─────────────────┐ │ Load Balancer │────│ Nginx │────│ Vue 3 App │ │ │ │ (Gateway) │ │ (Frontend) │ └───────────────┘ └──────────────┘ └─────────────────┘ │ ├─────┐ │ │ ▼ ▼ ┌─────────┐ ┌─────────────┐ │ API │ │ Auth │ │ Service │ │ Service │ └─────────┘ └─────────────┘ ``` ### Routing Challenges 1. **Frontend Routes**: `/`, `/dashboard`, `/profile` 2. **API Routes**: `/api/users`, `/api/orders` 3. **Service Routes**: `/auth/login`, `/auth/logout` ## Basic Configuration ### Simple Vue 3 Configuration ```nginx server { listen 80; server_name example.com; root /var/www/vue-app/dist; index index.html; # Handle Vue Router (client-side routing) location / { try_files $uri $uri/ /index.html; } # Static assets with caching 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; } } ``` ### With API Proxy ```nginx server { listen 80; server_name example.com; root /var/www/vue-app/dist; index index.html; # API proxy to microservices location /api/ { proxy_pass http://api-backend; 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; } # Frontend application location / { try_files $uri $uri/ /index.html; } } upstream api-backend { server api-service-1:3000; server api-service-2:3000; server api-service-3:3000; } ``` ## Advanced Patterns ### Multi-Environment Setup ```nginx # Development Environment server { listen 80; server_name dev.example.com; root /var/www/vue-app-dev/dist; location /api/ { proxy_pass http://dev-api-backend; } location / { try_files $uri $uri/ /index.html; } } # Production Environment server { listen 443 ssl http2; server_name example.com; root /var/www/vue-app-prod/dist; ssl_certificate /etc/ssl/certs/example.com.crt; ssl_certificate_key /etc/ssl/private/example.com.key; location /api/ { proxy_pass https://prod-api-backend; } location / { try_files $uri $uri/ /index.html; } } ``` ### Subdirectory Deployments ```nginx # Vue app deployed in subdirectory location /app/ { alias /var/www/vue-app/dist/; try_files $uri $uri/ /app/index.html; } # API services location /app/api/ { rewrite ^/app/api/(.*) /api/$1 break; proxy_pass http://api-backend; } ``` ### Multiple SPAs ```nginx server { listen 80; server_name example.com; # Admin Panel (Vue 3) location /admin/ { alias /var/www/admin-panel/dist/; try_files $uri $uri/ /admin/index.html; } # Customer Portal (Vue 3) location /portal/ { alias /var/www/customer-portal/dist/; try_files $uri $uri/ /portal/index.html; } # Main Website (Vue 3) location / { root /var/www/main-site/dist; try_files $uri $uri/ /index.html; } } ``` ## Real-world Examples ### E-commerce Microservices ```nginx server { listen 443 ssl http2; server_name shop.example.com; root /var/www/vue-shop/dist; # Product API location /api/products/ { proxy_pass http://product-service; } # User API location /api/users/ { proxy_pass http://user-service; } # Order API location /api/orders/ { proxy_pass http://order-service; } # Payment API location /api/payments/ { proxy_pass http://payment-service; } # Static assets optimization location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; try_files $uri =404; } # Vue 3 SPA - must be last location / { try_files $uri $uri/ /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; } } # Backend services upstream product-service { server product-api-1:3001; server product-api-2:3001; } upstream user-service { server user-api-1:3002; server user-api-2:3002; } upstream order-service { server order-api-1:3003; server order-api-2:3003; } upstream payment-service { server payment-api-1:3004; server payment-api-2:3004; } ``` ### Development vs Production ```nginx # Development Configuration map $http_host $environment { ~^dev\. "development"; ~^staging\. "staging"; default "production"; } server { listen 80; server_name ~^(?<subdomain>.+)\.example\.com$; set $app_root /var/www/vue-app-$environment/dist; root $app_root; # Development: Disable caching location ~* \.(js|css)$ { expires -1; add_header Cache-Control "no-cache, no-store, must-revalidate"; try_files $uri =404; } # Production: Aggressive caching location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; try_files $uri =404; } # API routing based on environment location /api/ { proxy_pass http://$environment-api-backend; } # Vue 3 SPA location / { try_files $uri $uri/ /index.html; } } ``` ## Best Practices ### 1. Order Matters ```nginx # ❌ Wrong - too broad location blocks first location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://backend; # This will never be reached! } # ✅ Correct - specific locations first location /api/ { proxy_pass http://backend; } location / { try_files $uri $uri/ /index.html; } ``` ### 2. Asset Optimization ```nginx # Cache static assets aggressively 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; # Enable gzip compression gzip_static on; } # Don't cache HTML files location ~* \.html$ { expires -1; add_header Cache-Control "no-cache, no-store, must-revalidate"; try_files $uri $uri/ /index.html; } ``` ### 3. Security Headers ```nginx location / { try_files $uri $uri/ /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 Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always; } ``` ### 4. Health Checks ```nginx # Health check endpoint location /health { access_log off; return 200 "healthy\n"; add_header Content-Type text/plain; } # Detailed status for monitoring location /nginx-status { stub_status on; access_log off; allow 127.0.0.1; allow 10.0.0.0/8; deny all; } ``` ### 5. Error Handling ```nginx # Custom error pages error_page 404 /index.html; # Let Vue Router handle 404s error_page 500 502 503 504 /50x.html; location = /50x.html { root /var/www/error-pages; } # Fallback for API errors location /api/ { proxy_pass http://backend; # Custom error handling proxy_intercept_errors on; error_page 502 503 504 /api-error.json; } location = /api-error.json { return 503 '{"error": "Service temporarily unavailable"}'; add_header Content-Type application/json; } ``` ## Troubleshooting ### Common Issues #### 1. 404 on Refresh **Problem**: Direct URL access returns 404 ``` GET /dashboard → 404 Not Found ``` **Solution**: Ensure try_files is configured correctly ```nginx location / { try_files $uri $uri/ /index.html; } ``` #### 2. API Calls Being Intercepted **Problem**: API calls are being served index.html ``` GET /api/users → Returns index.html instead of JSON ``` **Solution**: Place API location blocks before catch-all ```nginx location /api/ { proxy_pass http://backend; } location / { try_files $uri $uri/ /index.html; } ``` #### 3. Assets Not Loading **Problem**: Static assets return 404 ``` GET /assets/app.js → 404 Not Found ``` **Solution**: Check file paths and try_files configuration ```nginx location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { try_files $uri =404; # Don't fallback to index.html for assets } ``` ### Debugging Commands ```bash # Test nginx configuration nginx -t # Reload nginx nginx -s reload # Check access logs tail -f /var/log/nginx/access.log # Check error logs tail -f /var/log/nginx/error.log # Test specific URLs curl -I http://example.com/dashboard curl -I http://example.com/api/users ``` ### Debug Configuration ```nginx # Enable debug logging error_log /var/log/nginx/debug.log debug; server { # Log try_files attempts location / { try_files $uri $uri/ /index.html; # Debug headers add_header X-Debug-Uri $uri; add_header X-Debug-Args $args; add_header X-Debug-Request-Uri $request_uri; } } ``` ## Conclusion The `try_files` directive is crucial for serving Vue 3 SPAs in microservices architectures. Key takeaways: 1. **Order matters**: Place specific location blocks before catch-all blocks 2. **Assets need special handling**: Don't let static assets fall back to index.html 3. **Security**: Always include appropriate headers 4. **Caching**: Optimize static assets but not HTML files 5. **Monitoring**: Include health checks and proper logging With proper configuration, Nginx can efficiently serve Vue 3 applications while proxying API requests to appropriate microservices, creating a seamless user experience. --- *This tutorial provides a comprehensive guide to using nginx try_files with Vue 3 in modern microservices architectures. For production deployments, always test configurations thoroughly and follow security best practices.*