authwall-proxy-nginx

Several domains behind a single Authwall. Authwall authenticates every request, then forwards it to an nginx reverse proxy that routes each domain to its own app.

client → authwall → nginx → { apps, notes, echo }
flowchart LR
    client --> authwall --> nginx
    nginx --> apps
    nginx --> notes
    nginx --> echo

Authwall always has exactly one upstream. Here that upstream is the nginx router, and AUTHWALL_UPSTREAM_MODE=proxy makes Authwall preserve the client's original Host header so nginx can tell the domains apart.

Set up local hostnames

The example uses three domains, so add them to your hosts file — /etc/hosts on Linux/macOS, C:\Windows\System32\drivers\etc\hosts on Windows:

127.0.0.1 apps.mydomain.test
127.0.0.1 notes.mydomain.test
127.0.0.1 echo.mydomain.test

Run it

docker compose up

Open any of the three domains on port 3000:

Choose Sign up on any one of them and create an account. Because the session cookie is scoped to mydomain.test (AUTHWALL_COOKIE_DOMAIN), that one sign-in is valid across all three domains.

Each app is a jmalloc/echo-server that echoes the request back. The echoed Host header shows which domain reached which app — proof that Authwall preserved the Host and nginx routed on it.

How it works

  • authwall is the only published service (port 3000); all three domains resolve to it.
  • AUTHWALL_UPSTREAM_MODE=proxy tells Authwall to keep the client's original Host header when forwarding to its single upstream, http://nginx.
  • nginx.conf is the router: a map turns the request Host into the matching upstream, and a single server block proxies to it — or returns 404 for an unknown domain. It is the whole nginx config, mounted over /etc/nginx/nginx.conf.
  • AUTHWALL_COOKIE_DOMAIN=mydomain.test shares the session across the subdomains, so users sign in once.

What to change for your app

  • Replace the apps / notes / echo services with your own apps.
  • Edit the map $host $upstream entries in nginx.conf to match your domains.
  • For production, give the domains real DNS records, and terminate TLS at a load balancer in front of Authwall; set AUTHWALL_PUBLIC_URL to the https:// address.

Configuration files

docker-compose.yaml

services:

  # Authwall is the entrypoint and the only published service. It authenticates
  # every request, then forwards it to its single upstream — the nginx router.
  # AUTHWALL_UPSTREAM_MODE=proxy keeps the client's original Host header so nginx
  # can route by domain. AUTHWALL_COOKIE_DOMAIN shares the session across the
  # subdomains, so one sign-in covers all of them.
  authwall:
    image: vbarbarosh/authwall
    restart: unless-stopped
    environment:
      AUTHWALL_PUBLIC_URL: http://apps.mydomain.test:3000
      AUTHWALL_UPSTREAM_URL: http://nginx
      AUTHWALL_UPSTREAM_MODE: proxy
      AUTHWALL_COOKIE_DOMAIN: mydomain.test
    ports:
      - 3000:3000
    volumes:
      - ./data:/app/data
    depends_on:
      - nginx

  # Reverse proxy behind Authwall. Routes each domain to its own app by Host.
  nginx:
    image: nginx:alpine
    restart: unless-stopped
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - apps
      - notes
      - echo

  # Stand-in upstream apps, one per domain. echo-server echoes each request,
  # so the response shows which Host reached which app.
  apps:
    image: jmalloc/echo-server
    restart: unless-stopped

  notes:
    image: jmalloc/echo-server
    restart: unless-stopped

  echo:
    image: jmalloc/echo-server
    restart: unless-stopped

nginx.conf

events {}

http {
    # Docker internal DNS
    resolver 127.0.0.11 valid=10s;

    map $host $upstream {
        default "";

        apps.mydomain.test http://apps:8080;
        echo.mydomain.test http://echo:8080;
        notes.mydomain.test http://notes:8080;
    }

    server {
        listen 80;
        server_name _;

        location / {
            if ($upstream = "") {
                return 404;
            }

            proxy_pass $upstream;

            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;

            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }
    }
}