A simple, secure, and lightweight reverse proxy that adds a robust authentication layer in front of any web application. This image is designed to be placed behind an existing reverse proxy (like Traefik, Caddy, or another Nginx instance) that handles SSL/TLS termination.
This Docker image provides an Nginx-based reverse proxy that protects your backend services with a simple but effective authentication mechanism. Instead of relying on the browser to send an Authorization header with every request, it uses a secure, HttpOnly cookie. This means users log in once with their username and password, and are then authenticated for all subsequent requests until the cookie expires. It's an ideal solution for adding a quick and secure authentication gate to internal tools, dashboards, or any web service that is less trusted or lacks its own user management.
The authentication flow is designed to be both secure and user-friendly:
- First Visit: When a user first accesses the protected application, the proxy detects they don't have a valid authentication cookie.
- HTTP Basic Auth: The proxy challenges the user with an HTTP Basic Authentication prompt, requesting a username and password.
- Set Secure Cookie: Upon successful validation of the credentials against the configured
.htpasswdentry, the proxy sets a secure,HttpOnlycookie in the user's browser. This cookie contains a secret token known only to the proxy. - Authenticated Access: On all subsequent requests, the browser automatically sends the cookie. The proxy validates the cookie's token. If it's valid, the user is granted access without needing to log in again.
- Seamless Experience: The user remains logged in for 7 days, after which the cookie expires and they will be prompted to log in again.
This method is more secure than standard Basic Auth for web UIs because the user's credentials are only transmitted once during the initial login.
- Efficient Cookie-Based Auth: Log in once and stay authenticated for 7 days.
- Security Hardened:
- Runs as an unprivileged
nginxuser. - Sets secure headers by default:
X-Frame-Options,X-Content-Type-Options,Referrer-Policy, and a configurableContent-Security-Policy. - Uses secure cookie flags:
HttpOnly,Secure, andSameSite=Strict. - Configurable timeouts and buffer sizes to improve resilience.
- Runs as an unprivileged
- Highly Configurable: All key parameters are easily configured with environment variables.
- Lightweight & Performant: Built on the official
nginxinc/nginx-unprivileged:stable-alpineimage for a minimal footprint. - WebSocket Support: Seamlessly proxies WebSocket connections (
Upgradeheaders).
It is important to understand what this image does not do:
- No SSL/TLS Termination: This proxy does not handle HTTPS. It is designed to run behind another reverse proxy (e.g., Traefik, Caddy) that terminates SSL. The
Secureflag on the cookie requires the connection to the user's browser to be HTTPS. - Single Shared Cookie: The proxy uses one secret token (
SCA_TOKEN) for all users. This means every user who successfully logs in receives a cookie with the same value. This is ideal for a single user or a small group of trusted users sharing credentials (e.g., for a home lab). It does not provide per-user sessions. - No Session Invalidation: If an attacker steals a valid authentication cookie, they can use it to access the application until the cookie expires (after 7 days) or the
SCA_TOKENis changed on the server. There is no mechanism to remotely log out a specific session.
This example shows how to place the nginx-cookie-auth proxy in front of a web application within a Docker network.
Save this as docker-compose.yml:
version: '3.8'
services:
nginx-cookie-auth:
image: mon4d/nginx-cookie-auth:latest
container_name: nginx-cookie-auth
restart: unless-stopped
# By design, ports are not exposed to the host.
# Access should be managed by your main reverse proxy (e.g. Traefik, Caddy, or another Nginx instance).
# For quick testing, you can uncomment the ports section below.
# ports:
# - "8080:8080"
environment:
# --- Required ---
# Generate with: htpasswd -nbB testuser testpassword
- HTPASSWD=testuser:$$apr1$$D7q61h1b$$C8v6bB5gYJGpMRwIu.oud/
# Generate with: openssl rand -hex 32
- SCA_TOKEN=_a_very_secret_and_long_random_string_change_me_
# --- Target Service ---
# If on the same docker network, use the container-name and port.
- FORWARD_HOST=webapp
- FORWARD_PORT=80
# --- Optional Hardening ---
# - CLIENT_MAX_BODY_SIZE=32m
# - MAX_BUFFER_SIZE=16k
# - TIMEOUT=60
# - CONTENT_SECURITY_POLICY="default-src 'self'; frame-ancestors 'self'; form-action 'self';"
depends_on:
- webapp
# Example web app to be protected:
webapp:
image: nginxdemos/hello:plain-text
container_name: webapp
restart: unless-stopped
To run this stack:
- Generate and replace the
HTPASSWDandSCA_TOKENvalues in the file. - Start the services:
docker-compose up -d. - Recommended Setup: Configure your main reverse proxy (e.g. Traefik, Caddy, or another Nginx instance) to forward traffic for your desired domain to
http://nginx-cookie-auth:8080. - For Quick Local Testing: If you don't have a main reverse proxy, uncomment the
portssection indocker-compose.ymland rundocker-compose up -dagain. You can then access the service athttp://localhost:8080(though you will see browser warnings about theSecurecookie flag on an insecure connection).
You must provide two secret values for the proxy to operate.
This variable holds the username and hashed password. Use htpasswd to generate it.
# The -n flag prints to stdout, -b runs in batch mode, -B uses bcrypt (recommended)
htpasswd -nbB myuser mypassword123
# Output: myuser:$2y$05$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxCopy the entire output string and use it as the value for the HTPASSWD environment variable.
This is the secret value for the authentication cookie. It should be a long, random string.
openssl rand -hex 32
# Output: a1b7c3d9e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1This image is designed for simplicity. I encourage you to review the source code on my GitHub page to see how everything fits together.
Future enhancements may include:
- Configurable Cookie Lifetime: An environment variable (
SCA_TOKEN_MAX_AGE) to allow customization of the cookie's expiration time, which is currently hardcoded to 7 days. - Per-User Sessions: A more advanced (and complex) implementation could generate unique session tokens for each user, allowing for individual session invalidation.
All configuration is managed via environment variables.
| Variable | Description | Default Value |
|---|---|---|
HTPASSWD |
(Required) The htpasswd entry for authentication. Format: user:hashed_password. |
(none) |
SCA_TOKEN |
(Required) The secret token for the authentication cookie. Should be a long, random string. | (none) |
FORWARD_HOST |
The hostname or container name of the upstream service to proxy requests to. | web |
FORWARD_PORT |
The port of the upstream service. | 80 |
CLIENT_MAX_BODY_SIZE |
The maximum allowed size of the client request body. Useful for file uploads. | 256m |
TIMEOUT |
A general timeout in seconds for proxy, send, and client operations. | 60 |
MAX_BUFFER_SIZE |
The buffer size for client request body and headers. | 16k |
CONTENT_SECURITY_POLICY |
The Content-Security-Policy header value. Set to "" to disable. A sane default is recommended. |
"" (empty) |