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 --> echoAuthwall 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.testRun it
docker compose upOpen 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
authwallis the only published service (port 3000); all three domains resolve to it.AUTHWALL_UPSTREAM_MODE=proxytells Authwall to keep the client's originalHostheader when forwarding to its single upstream,http://nginx.nginx.confis the router: amapturns the requestHostinto the matching upstream, and a singleserverblock proxies to it — or returns404for an unknown domain. It is the whole nginx config, mounted over/etc/nginx/nginx.conf.AUTHWALL_COOKIE_DOMAIN=mydomain.testshares the session across the subdomains, so users sign in once.
What to change for your app
- Replace the
apps/notes/echoservices with your own apps. - Edit the
map $host $upstreamentries innginx.confto 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_URLto thehttps://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-stoppednginx.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 "";
}
}
}