Deploy Basic Auth Proxy — Password-Protect Any Service
Put any service behind a username & password — nginx proxy
nginx-password-auth
Just deployed
Deploy and Host Basic Auth Proxy on Railway
Basic Auth Proxy is a lightweight nginx reverse proxy that puts any service behind a username and password — no code changes to the service itself. Point it at an app, dashboard, API, or staging site that has no login of its own, set a username and password, and it adds an HTTP Basic Authentication gate in front. Built on Alpine Linux for a tiny footprint, it's the fastest way to lock down something that was never built to be public.
What This Template Deploys
| Service | Purpose |
|---|---|
| Basic Auth Proxy | An nginx reverse proxy that adds HTTP Basic Auth in front of an upstream service |
A single, minimal Alpine-based container. It holds no database and no volume — it forwards authenticated requests to the service you point it at and rejects everything else with a password prompt.
About Hosting
Plenty of useful things weren't built with a login screen — an internal dashboard, a staging deploy, a metrics UI, a quick API. Making them public is risky; building auth into each one is overkill. This proxy is the middle path: a password gate you drop in front, in minutes.
Point ORIGIN at a private service. Set ORIGIN to the upstream you want to protect — ideally a Railway private domain like http://myapp.railway.internal:3000, so the real service is never exposed to the public internet at all and only this proxy can reach it. Requests that pass the password check are forwarded; everything else gets a 401. You can also proxy an external URL if you need to add a gate in front of something already public.
Set BASIC_AUTH to your credentials. The format is username:password. The proxy generates the password file at startup, so there's no htpasswd tooling to run — just set the variable. Combined with Railway's automatic HTTPS, the credentials are encrypted in transit.
Know what Basic Auth is and isn't. HTTP Basic Authentication is a shared username and password, not a full identity system — there are no per-user accounts, no MFA, and no session management. Over HTTPS it's perfectly good for gating a staging site, an internal tool, or an admin panel that just needs to be kept out of public hands. For per-user logins, SSO, or MFA, use a full identity provider instead. This is the simple, fast option for the common case.
Avoid the no-auth override in production. The image supports an ALLOW_NO_AUTH=true flag that runs the proxy with no password at all. That's for local testing only — never set it on a public deployment, or you've built an open proxy to your service.
Typical cost: ~$5/month or less on Railway — the Alpine image is tiny and the proxy is nearly free to run. It's open source and free.
How It Compares
| Basic Auth Proxy | Full IdP (Authentik/Logto) | App's own auth | No protection | |
|---|---|---|---|---|
| Setup effort | Minutes, two variables | Moderate | Per-app coding | None |
| Per-user accounts | No (shared login) | Yes | Depends | N/A |
| MFA / SSO | No | Yes | Depends | No |
| Code changes to service | None | Usually none | Yes | None |
| Best for | Staging, internal, admin gates | Teams, production SSO | Purpose-built apps | Public content |
| Self-hostable | Yes | Yes | N/A | N/A |
A full identity provider gives per-user accounts, MFA, and SSO but takes more setup. Building auth into the app means code changes. Leaving it open isn't an option for anything sensitive. Basic Auth Proxy's niche is the quick gate — a shared password in front of something that just needs to be kept private, with zero changes to the service.
Deploy in Under 5 Minutes
- Click Deploy on Railway — the proxy builds automatically (~1 minute; the image is tiny)
- Set
ORIGINto the service you want to protect (ideally its Railway private domain) - Set
BASIC_AUTHtousername:password - Optionally set
BASIC_AUTH_REALMto customize the login prompt text - Open the proxy's public URL — you'll be asked for the credentials, then forwarded to the service
Point your users at the proxy's URL instead of the service's, and the service stays private behind it.
Common Use Cases
- Protect a staging site — keep a pre-release deploy private with a shared password instead of exposing it publicly
- Gate an internal dashboard — lock down a Grafana, admin panel, or metrics UI that has no login of its own
- Add auth to an API — put a quick password in front of an internal API endpoint without changing its code
- Lock down a demo — share a work-in-progress with a client behind a simple credential
- Restrict a tool with no auth — front any service that was never built to be internet-facing
- Private-network gateway — expose one authenticated entry point while the real service stays on Railway's private network
Configuration
| Variable | Required | Description |
|---|---|---|
ORIGIN | Required | The upstream URL to proxy, e.g. http://myapp.railway.internal:3000 |
BASIC_AUTH | Required | Credentials as username:password |
BASIC_AUTH_REALM | Optional | Text shown in the browser login prompt, e.g. Staging Environment |
PORT | Pre-set | Public port the proxy listens on |
ALLOW_NO_AUTH | Optional | true disables auth entirely — testing only, never in production |
Point
ORIGINat a private service. Use the upstream's Railway private domain so the real service isn't publicly reachable and only the proxy can access it. The proxy becomes the single authenticated entry point.
Never set
ALLOW_NO_AUTH=truein production. It runs the proxy with no password at all, creating an open gateway to your service. It exists only for local testing.
Dependencies for Basic Auth Proxy Hosting
- Railway account — a few dollars a month for the tiny proxy
- A service to protect, reachable from the proxy (ideally on Railway's private network)
- No database, volume, or additional tooling required
Deployment Dependencies
- railway-nginx-password-auth Repository
- nginx HTTP Basic Authentication
- Railway Private Networking
- Railway Environment Variables
Implementation Details
The template runs a small Alpine-based nginx image configured as a reverse proxy with HTTP Basic Authentication. On startup it reads BASIC_AUTH (in username:password form) and generates the nginx password file, then serves as an authenticating front end for the URL set in ORIGIN. Requests that pass authentication are proxied upstream; unauthenticated requests receive a 401 challenge, prompting the browser's login dialog.
The recommended pattern on Railway is to set ORIGIN to the protected service's private domain (*.railway.internal), so the service has no public domain of its own and is reachable only through the proxy. This turns the proxy into the single public, authenticated entry point while the backend stays on the private network. Railway's automatic HTTPS secures the credentials in transit, which matters because Basic Auth transmits them on every request.
Because the proxy is stateless and tiny, it starts almost instantly and adds negligible overhead. An ALLOW_NO_AUTH escape hatch exists for local testing without credentials and should never be enabled on a public deployment.
Frequently Asked Questions
What does this actually do? It puts a username-and-password prompt in front of any service that doesn't have one. You point it at your service, set credentials, and users must authenticate before reaching it — no changes to the service itself.
How do I keep the real service private? Set ORIGIN to the service's Railway private domain (*.railway.internal) and don't give the service its own public domain. Then only the proxy can reach it, and only authenticated users reach the proxy.
Is Basic Auth secure enough? Over HTTPS (which Railway provides), it's fine for gating staging sites, internal tools, and admin panels with a shared password. It has no per-user accounts or MFA, so for production user logins use a full identity provider instead.
Can I protect a service that's already public? Yes — set ORIGIN to its public URL to add a password gate in front, though the strongest setup keeps the backend private.
How is this different from a full auth system? A full identity provider offers per-user accounts, SSO, and MFA but needs more setup. This is a shared password added in minutes with two variables — the right tool when you just need to keep something private.
What is BASIC_AUTH_REALM? The text shown in the browser's login popup. Set it to something like Staging Environment so users know what they're logging into.
Does it slow down my service? No. The proxy is a tiny stateless Alpine container that adds negligible latency between the user and your service.
Why Deploy Basic Auth Proxy on Railway?
Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.
By deploying Basic Auth Proxy on Railway you get an instant password gate for anything — a tiny nginx proxy, two variables to configure, and automatic HTTPS, with the protected service kept private behind it. Lock down a staging site, an internal dashboard, or an unprotected API in minutes, on infrastructure you own.
Template Content
nginx-password-auth
biw/railway-nginx-password-authORIGIN
The URL that is proxied. i.e. express.railway.local:3000
BASIC_AUTH
username:password

