---
# System prepended metadata

title: Nginx try_files in Modern Microservices with Vue 3
tags: ['#CloudNative']

---

# 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.*