Protect Internal Dashboards with Authelia, Nginx, and an Identity-Aware Gate

Add a real login wall in front of internal dashboards without turning a private admin tool into a casually public website.

AutheliaNginx auth gatePrivate admin access
What you learn

How to keep the dashboard private at the network layer, place Nginx in front of it, integrate Authelia for authentication, and verify that unauthenticated traffic never reaches the app directly.

Best for

Grafana, Portainer, internal OpenClaw dashboards, self-hosted admin panels, and any web UI that a small team needs occasionally but should not leave open to the internet.

Risk to watch

The most common mistake is protecting the proxy path while the real app still listens publicly on its own port.

Before you begin

  • A dashboard or admin app already running, ideally bound to 127.0.0.1 or reachable only on an internal Docker network.
  • Nginx available as the reverse proxy you control.
  • A domain or subdomain for the protected entry point, such as dash.example.com.
  • A place to store Authelia configuration, user definitions, and persistent session storage.

Authelia’s documentation recommends understanding your proxy layout and customizing examples to your environment. The safe baseline is simple: keep the real dashboard private, and make Nginx plus Authelia the only public doorway.

Reference note: Current Authelia docs describe Nginx integration through forwarded authentication and recommend explicit access-control policy design rather than broad allow-by-default rules.

Why this pattern matters

SSH tunnels are excellent for one admin at a time, but teams often need a more usable login wall for browser-based tools. An identity-aware gate gives you that convenience without teaching yourself bad habits like exposing Grafana or Portainer directly on the public internet.

Authelia is a strong fit because it is free, open-source, and designed to work with reverse proxies. The point is not just “add auth.” The point is to preserve a private upstream and make authentication happen before the request reaches the dashboard.

Step 1: Keep the upstream dashboard private

First verify where the real app listens. A safer result is localhost or an internal-only network target, not a world-open port.

ss -tulpn | grep ':3000'
curl -I http://127.0.0.1:3000

If the app currently binds to 0.0.0.0, change that first or place it on an internal Docker network with no published port. For a Compose service, that often means removing the public ports: section and letting only Nginx connect to it over a shared network.

Warning: If the upstream is still public, Authelia becomes optional decoration instead of a true security boundary.

Step 2: Run Authelia with explicit configuration

Authelia can run beside Nginx in Docker Compose. The exact deployment varies, but keep the structure deliberate:

services:
  authelia:
    image: authelia/authelia:latest
    volumes:
      - ./authelia/config:/config
    networks:
      - proxy
    restart: unless-stopped

At minimum you need Authelia configuration, an authentication backend such as a local users file or LDAP, session storage, and access-control rules. For small private stacks, a file-based user backend is often enough to start cleanly.

Set the access-control default policy to deny unless a rule says otherwise. Authelia’s configuration reference recommends deny as the default because it is much safer than accidentally allowing new resources.

Step 3: Put Nginx in front of the dashboard

Nginx should be the public entry point, not the dashboard container. A common layout is:

  • dash.example.com points to Nginx
  • Nginx performs an auth request to Authelia
  • Nginx proxies approved traffic to the private dashboard upstream

Authelia’s current Nginx integration docs show the forwarded-auth pattern. The exact snippets depend on your TLS and vhost structure, but the shape looks like this:

location /authelia {
    proxy_pass http://authelia:9091;
}

location / {
    auth_request /authelia-auth;
    proxy_pass http://dashboard-upstream;
}

Do not copy fragmentary examples blindly. Match header forwarding, trusted proxy settings, and upstream names to your actual layout, then test them intentionally.

Step 4: Add access rules and test the whole gate

Define the specific protected domain in Authelia’s access-control rules. Keep policies narrow. For example, protect only the dashboard hostname and do not route unrelated sites through the same auth wall unless that is a deliberate design choice.

Then test three paths:

  1. Unauthenticated browser access should redirect to Authelia instead of loading the dashboard.
  2. Successful login should return you to the dashboard.
  3. Direct access to the upstream port should fail from outside the host or proxy network.
curl -I https://dash.example.com
curl -I http://127.0.0.1:3000
journalctl -u nginx -n 50 --no-pager

Expected outcome and verification

By the end of this setup, you want all of these to be true:

  • The dashboard is reachable at the friendly public hostname.
  • Unauthenticated requests are intercepted by Authelia.
  • The real app is still private behind Nginx.
  • Access rules default to deny unless you added an explicit allow rule.
  • Nginx and Authelia logs show clean auth flow instead of loops or bypasses.

If you administer the service from a laptop over SSH as well, keep that tunnel-only path available during the cutover so you can recover without relying on the new gate.

Troubleshooting common problems

You get redirect loops.
Check trusted-proxy headers, callback URLs, cookie domain settings, and whether the auth endpoint itself is being re-protected.

Login works but the dashboard is blank or broken.
Inspect Nginx headers and the upstream base URL. Some apps need X-Forwarded-Proto, host headers, or a matching root URL setting.

The dashboard is still reachable directly.
Fix the upstream binding or Docker port publishing. This is the most important failure to correct.

Authelia works for one domain but blocks others unexpectedly.
Your default deny policy is doing its job. Add explicit rules only for the hostnames that should be protected.

Caution: A login wall is not the same as a public-facing SaaS hardening posture. Keep internal tools private by design even when SSO or MFA sits in front of them.

What to do next

Continue with How to Use SSH Port Forwarding for Secure Access to Private Web UIs and Databases.