Nginx: Direct Request Routing Between Named Locations Without try_files


1 views

When working with Nginx configurations, developers often need to route requests directly from one location block to another named location (@location) without file existence checks. While try_files is commonly used for this purpose, it requires at least two arguments and forces unnecessary filesystem checks when we simply want to route requests.

The current implementation using:

location /api/ {
  try_files @app;
}

Is syntactically incorrect because try_files requires at least two parameters. While we could use:

location /api/ {
  try_files /nonexistent @app;
}

This creates an unnecessary filesystem check that impacts performance.

1. Using error_page Directive

A cleaner approach is using error_page with HTTP 403:

location /api/ {
  error_page 403 = @app;
  return 403;
}

This immediately routes to the named location without filesystem checks.

2. Rewrite + Named Location

Another pattern using rewrite:

location /api/ {
  rewrite ^ /@app last;
}

location @app {
  # proxy configuration
}

3. Direct proxy_pass (Simplest Solution)

If your named location only contains proxy directives, you can directly use:

location /api/ {
  proxy_pass_request_headers on;
  proxy_set_header ...;
  proxy_pass http://app;
}

Each method has different performance characteristics:

  • try_files with dummy file: 1 filesystem stat per request
  • error_page method: No filesystem access
  • rewrite method: Minimal regex processing

Here's a complete production-ready example:

upstream backend {
  server 127.0.0.1:9000;
}

server {
  listen 80;
  
  location /static/ {
    root /var/www;
  }
  
  location /api/ {
    # Method 1: error_page approach
    error_page 418 = @backend;
    return 418;
    
    # Or Method 2: rewrite approach
    # rewrite ^ /@backend last;
  }
  
  location @backend {
    proxy_pass http://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;
  }
}

When working with Nginx configuration, developers often need to route requests directly from one location block to another named location. The conventional approach using try_files feels like a workaround when you don't actually need file existence checking.

# The common but suboptimal approach
location /api/ {
    try_files non_existent_file @app; # Requires dummy file parameter
}

The most idiomatic alternative to try_files for this specific case is using error_page with 403 status code:

location /api/ {
    # Your custom configuration here
    error_page 403 = @app;
    return 403;
}

This pattern creates a clean internal redirect without any file system checks. The 403 status is just a vehicle for the redirect and won't actually appear to clients.

Both methods are efficient, but the error_page approach has some advantages:

  • No dummy file parameter required
  • More semantically correct for this use case
  • Same internal redirect mechanism as try_files
  • Minimal overhead (just one extra directive)

Here's how this fits into a complete server configuration:

upstream app_backend {
    server unix:/tmp/app.sock fail_timeout=0;
}

server {
    listen 443 ssl;
    server_name example.com;

    # Static file handling
    location / {
        try_files $uri $uri/ =404;
    }

    # API endpoint direct routing
    location /api/ {
        # Custom API-specific config
        add_header 'API-Version' '1.0';
        
        # Clean direct routing to named location
        error_page 403 = @app;
        return 403;
    }

    # Named location for proxy pass
    location @app {
        proxy_pass http://app_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        # Additional proxy settings...
    }
}

For completeness, here's another method using rewrite, though it's less preferred than error_page:

location /api/ {
    rewrite ^ @app last;
}

While this works, it's generally better to reserve rewrite for actual URL rewriting needs rather than simple internal routing.