Nginx location based on uri path

nginx

I have a domain, call it example.com, with paths /test1 and /test2.

Requests for http example.com/test1 are fed to test1.example.com/test1.

Requests for http example.com/test2 are fed to test2.example.com/test2.

This nginx config has worked fine for my requirements to this point.

server {

  listen 80 default_server;
  root /usr/share/nginx/html;
  server_name localhost;
  index index.html;
  autoindex on;

  location /test1 {
    resolver ns1.example.com;
    resolver_timeout 5s;
    proxy_pass http://test1.example.com/test1;
    proxy_redirect default;
  }

  location /test2 {
    resolver ns1.example.com;
    resolver_timeout 5s;
    proxy_pass http://test2.example.com/test2;
    proxy_redirect default;
  }

}

I now need to alter the config if possible so that requests make it to the appropriate backend without being individually added to the nginx config.

Using sub domains instead of path is a long term option but not feasible at this time due to other constraints.

I would rather the solution be contained within nginx config and not rely on maintaining a database of current example.com hostnames if at all possible.

Data in uri path after first slash and before the second (if it exists) is what I need to key on.

For example: example.com/debug/index.html.
debug would be parsed out. debug may or may not be a valid hostname in the example.com domain. If it is not valid (404, 500, or 502 error.. maybe others) then drop to a catch-all page. If it is valid then direct the request to debug.example.com/debug/index.html.

This is my first attempt but doesn't work (too many redirects error and images/includes looked for in local file system instead of remote though the basic index.html is found/loaded).

Does anyone know what I may be missing or know of alternatives that will help me reach my goal?

server {

  listen 80 default_server;
  root /usr/share/nginx/html;
  server_name localhost;
  index index.html;
  autoindex on;

  set $frontend_fqdn "nil";
  set $namespace "nil";

  if ($request_uri ~ "^/([A-Za-z0-9-_]+).*$") {
    set $namespace "$1";
    set $frontend_fqdn "$namespace.example.com";
  }

  location / {
    resolver ns1.example.com;
    resolver_timeout 5s;
    error_page 404 500 502 = @fallback;

    if ($frontend_fqdn !~ "nil") {
      proxy_pass http://$frontend_fqdn$request_uri;
    }
    try_files $uri $uri/ =404;
  }

  location @fallback {
    rewrite ^(.*)$ $scheme://$host permanent;
  }

}

Thank you in advance for any help.

Best Answer

Something like this should work:

server {

  listen 80 default_server;
  root /usr/share/nginx/html;
  server_name localhost;
  index index.html;
  autoindex on;

  location ~ ^/([^/]+) {
    try_files $uri $uri/ @proxy;
  }

  location @proxy {
    resolver ns1.example.com;
    resolver_timeout 5s;
    error_page 404 500 502 = @fallback;
    proxy_pass $scheme://$1.$host;
  }

  location @fallback {
    return 301 $scheme://$host;
  }

}

The first location block will match all URIs and save the first segment of the path to $1. It then tries to serve a matching local file/folder, if one exists, otherwise, it attempts to proxy to a server as you mentioned in your question by injecting the path segment as a subdomain. If that fails, it redirects to a predetermined location.