README.md
Rama main · réplica de lectura del repositorio original.
<div align="center">
```
/$$ /$$ /$$
|__/ | $$ |__/
/$$$$$$$ /$$$$$$ /$$ /$$$$$$$ /$$ /$$ /$$ /$$ /$$ /$$$$$$ | $$$$$$$ /$$ /$$ /$$
| $$__ $$ /$$__ $$| $$| $$__ $$| $$ /$$//$$$$$$| $$ | $$ | $$ /$$__ $$| $$__ $$| $$ | $$| $$
| $$ \ $$| $$ \ $$| $$| $$ \ $$ \ $$$$/|______/| $$ | $$ | $$| $$$$$$$$| $$ \ $$| $$ | $$| $$
| $$ | $$| $$ | $$| $$| $$ | $$ >$$ $$ | $$ | $$ | $$| $$_____/| $$ | $$| $$ | $$| $$
| $$ | $$| $$$$$$$| $$| $$ | $$ /$$/\ $$ | $$$$$/$$$$/| $$$$$$$| $$$$$$$/| $$$$$$/| $$
|__/ |__/ \____ $$|__/|__/ |__/|__/ \__/ \_____/\___/ \_______/|_______/ \______/ |__/
/$$ \ $$
| $$$$$$/
\______/
```
### Manage your nginx reverse proxies from a modern web UI
**A lightweight, dependency-free web interface** for nginx reverse-proxy management — sites, TLS, redirects, config editing, `nginx -t`, reload/restart, logs and backup, exactly like you do from the shell. Zero rebuilding, no database, ~40 MB RAM.
<!-- badges (static, no network lookups) -->








One command install, or run it from a container. Manages **bare-metal nginx** or
nginx running on the same host from Docker — through a tiny host-side control
agent. Web UI frontends with automatically-guessed Let's Encrypt-friendly TLS paths.
</div>
---
## Install — one line
Copy-paste this. No cloning, no setup — the installer bootstraps itself, then asks
which deployment you want:
```bash
curl -fsSL https://raw.githubusercontent.com/himalsimkhada/nginx-webui/main/install.sh | bash
```
> Not ready yet? `./install.sh --check` does a safe dry run and reports what the
> installer would detect on your machine without changing anything.
---
## Table of Contents
- [Why nginx-webui?](#why-nginx-webui)
- [Features](#features)
- [Install & quick start](#quick-start)
- [Requirements](#requirements)
- [Deployment options](#deployment-options)
- [1. Docker + host control agent](#option-1--docker--host-control-agent)
- [2. Manual (bare-metal)](#option-2--manual-bare-metal)
- [Configuration](#configuration)
- [The `.env` in the UI](#the-env-in-the-ui)
- [Security notes](#security-notes)
- [API reference](#api-reference)
- [Development](#development)
- [Project structure](#project-structure)
- [How it works](#how-it-works)
- [License](#license)
---
## Why nginx-webui?
Managing nginx normally means SSH-ing in, hand-writing `server` blocks that are
easy to get wrong, running `nginx -t`, remembering `nginx -s reload`, and
tailing logs by hand. This project gives you a polished, single-page dashboard
for daily reverse-proxy chores — while **touching nothing** about how nginx
runs underneath.
It deals only with the same config files and the same commands real admins use
(`nginx -t`, `nginx -s reload`, `systemctl restart nginx`). Your nginx stays
yours; the UI just makes it pleasant.
---
## Features
| | |
|---|---|
| **Dashboard** | Live server state with stat boxes — version, running/master PID, workers, config file — and reload / check / restart buttons. The status card shows which transport answered (**host agent / http probe / local `/proc`**). |
| **Reverse-proxy wizard** | Create a site in one flat form: application name, domain (`server_name`), upstream (`proxy_pass`), HTTP **and** HTTPS ports, websocket upgrade, max upload size, upstream timeout. |
| **TLS made simple** | Enable TLS and the HTTPS port appears right in the TLS section; pick a saved certificate or type paths manually. Auto-defaults to 443, changeable. Optional `Redirect HTTP → HTTPS`. |
| **Saved SSL store** | Named certificate entries reused across sites — stored as **file paths** or as **pasted PEM content** (written where both host nginx and the container can read them), with CRUD in a dedicated card. |
| **Sites** | List, create, read, edit (form **or** raw config), delete, enable/disable (`sites-available` ↔ `sites-enabled`). |
| **Config editor** | Browse/edit any file under `/etc/nginx` (path-traversal safe). `nginx.conf` opens **read-only** until you click Edit; the nginx-webui `.env` is editable too. |
| **Validation & control** | `nginx -t`, reload (`nginx -s reload`), restart (`systemctl restart nginx`) — on the **host** nginx even when the UI runs in a container. |
| **Backup & restore** | One-click config tarball; restore validates with `nginx -t` and rolls back on failure. |
| **Log viewer** | Tail nginx error/access logs with line-count control and text filtering. |
| **Access protection** | Shared password (`WEBUI_PASSWORD`), 30-minute *remember me* session, log-out button, brute-force lockout (5 failures → 15 min block). |
| **Dark & light mode** | Theme toggle, persisted in the browser. |
| **Feather-light** | Flask + vanilla HTML/CSS/JS. No build step, no Node.js, no database — ~40 MB RAM. |
---
## Quick start
Install in one command — the installer **bootstraps itself** when streamed, cloning the repo before it runs:
```bash
curl -fsSL https://raw.githubusercontent.com/himalsimkhada/nginx-webui/main/install.sh | bash
```
Or clone and run directly:
```bash
git clone https://github.com/himalsimkhada/nginx-webui.git
cd nginx-webui
./install.sh
```
You'll be asked which deployment you want:
```
1) Docker + host agent - web UI container managing nginx on THIS machine (recommended)
2) Manual - nginx and the web UI both installed directly on THIS machine
```
> **Dry run first:** `./install.sh --check` reports what the installer detects
> on your machine (distro, nginx, host agent, Docker, python3) without changing
> a thing.
>
> **Dashboard included:** the installer detects when the companion webui admin
> facade ([himalsimkhada/webui](https://github.com/himalsimkhada/webui)) is not
> running and offers to install it for you — the portal hosts the dashboard UI,
> while this service stays API-only.
---
## Requirements
- Linux with nginx installed (`apt install nginx`), `nginx -t`-clean config
- Python 3.10+ (manual mode only)
- `sudo` access (for config files and `nginx` control)
- Docker is **optional** — needed only for the containerized deployment
Supported distros: Debian/Ubuntu (apt), RHEL/Fedora (dnf), Arch (pacman).
---
## Deployment options
| Option | What runs where | When to pick it |
|---|---|---|
| **1. Docker + host agent** | Web UI in a container, nginx on the host | You keep your existing nginx, UI stays containerized |
| **2. Manual** | Everything on this machine, systemd service | Minimal footprint, single server |
### Option 1 — Docker + host control agent
The web-UI container mounts `/etc/nginx` and `/var/log/nginx` from the host —
config editing, site management and `nginx -t` work straight from it. But a
container lives in another PID namespace and has no `systemctl`, so it **cannot
signal or restart the host nginx master**. For that, a tiny host-side
control agent (`host_agent.py`, standard library only) runs as a systemd
service on the host:
```bash
# ON THE NGINX HOST — one time
sudo cp host_agent.py /usr/local/bin/nginx-webui-host-agent.py
sudo chmod 755 /usr/local/bin/nginx-webui-host-agent.py
sudo cp nginx-webui-host-agent.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now nginx-webui-host-agent
```
It binds `0.0.0.0:9401` (so containers reach it via `host.docker.internal`);
restrict with a firewall if the host is on a LAN. API (JSON): `GET /status`,
`POST /check`, `POST /reload`, `POST /restart`. The installer (mode 1) does all
of this for you, writes `.env`, then:
```bash
cp .env.example .env
docker compose up -d
```
`.env` sets `NGINX_CTL_URL=http://host.docker.internal:9401`, so status, check,
reload and restart all act on the **host** nginx. The compose file adds
`extra_hosts: host.docker.internal → host-gateway`; if your Docker lacks
`host-gateway`, put the host's LAN IP in `NGINX_CTL_URL` instead.
### Option 2 — Manual (bare-metal)
Everything on this one machine, managed as a systemd service. Web UI at
`http://localhost:8400`.
```bash
./install.sh # choose 2) Manual
```
By hand:
```bash
python3 -m venv venv
./venv/bin/pip install -r requirements.txt
WEBUI_PASSWORD='your-password' SECRET_KEY='a-long-random-string' ./venv/bin/python app.py
```
As a boot-starting service (running as root, no sudo rules needed):
```bash
sudo cp nginx-webui.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now nginx-webui
```
If you prefer to run the service as an unprivileged user, add passwordless rules:
```bash
echo 'himal ALL=(ALL) NOPASSWD: /usr/sbin/nginx, /usr/bin/systemctl restart nginx' > /etc/sudoers.d/nginx-webui
```
---
## Configuration
All container settings live in `.env` (see `.env.example`); bare-metal uses
environment variables or the systemd `EnvironmentFile`:
| Variable | Default | Purpose |
|----------|---------|---------|
| `NGINX_CONF_DIR` | `/etc/nginx` | nginx config directory (files, sites, SSL store) |
| `NGINX_WEBUI_PORT` | `8400` | API listen port |
| `NGINX_STATUS_URL` | *(empty)* | HTTP probe of a reachable nginx `stub_status` for status-only checks |
| `NGINX_CTL_URL` | *(empty)* | Host control agent URL for status/check/reload/restart of the **host** nginx (containers) |
| `NGINX_WEBUI_ENV_FILE` | `/etc/nginx-webui.env` | Where the `.env` shown in the UI lives |
| `SSL_STORE_FILE` | `$NGINX_CONF_DIR/webui-ssl-store.json` | Saved certificate store |
| `SSL_CERTS_DIR` | `$NGINX_CONF_DIR/ssl` | Where pasted PEM content is written |
| `LOG_FILE` | *(auto)* | Log file to tail when journalctl isn't available (containers) |
| `NGINX_TIMEOUT` | `10` | Command timeout (s) for nginx operations |
| `WEBUI_PASSWORD` | *(empty = auth off)* | Single shared password required to use the UI |
| `SECRET_KEY` | *(dev default)* | Secret used to sign the session cookie; set a random value |
| `MAX_LOGIN_ATTEMPTS` / `LOCK_TIME` | `5` / `900` | Brute-force lockout config |
**Status source priority:** `NGINX_CTL_URL` (host agent) → `NGINX_STATUS_URL`
(HTTP probe) → local `/proc` + direct commands (bare-metal).
> **Access protection:** set `WEBUI_PASSWORD` to require a password at login.
> The *Remember me* checkbox persists the session for **30 minutes** then
> auto-logs-out (otherwise the session ends when the browser closes). A **Log
> out** button lives in the nav bar. If the variable is empty, authentication
> is disabled entirely.
---
## The `.env` in the UI
The file holding `NGINX_STATUS_URL` / `NGINX_CTL_URL` / etc. shows up as
**`.env`** in the dashboard's Config Files editor, so you can see and change it
without SSH. Bare metal: `/etc/nginx-webui.env` (the systemd `EnvironmentFile`).
Docker: `./.env` next to `docker-compose.yml` is mounted there. Edits apply
after restarting the service or `docker compose up -d`.
---
## Security notes
- Authentication is a single shared password compared against the configured `WEBUI_PASSWORD` — nothing stored on disk, no user database.
- Sessions use a signed cookie (set `SECRET_KEY`!), with **brute-force lockout** (5 failed logins → 15 min block) on the login form.
- Config file access is path-traversal safe and confined to `NGINX_CONF_DIR` (the `.env` being the single deliberate exception).
- The host control agent is fire-and-forget: bind `0.0.0.0` only for Docker reachability, restrict with a firewall, and it exposes an intentionally tiny API.
- `nginx.conf` opens read-only and requires an explicit Edit click to change.
- Restore writes go through the same validation gate nginx itself uses (`nginx -t`), with rollback on failure.
- No build step, no runtime downloads, no telemetry, no analytics.
---
## API reference
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/api/login` | Login (shared password) |
| GET | `/api/session` | Auth state |
| GET | `/api/system` | Service identity |
| GET | `/api/status` | Version, pid, workers, config file |
| GET · POST | `/api/check` | `nginx -t` validation |
| GET · POST | `/api/control/check` | Alias of `/api/check` (same pattern as reload/restart) |
| POST | `/api/control/reload` | `nginx -s reload` (host agent or local) |
| POST | `/api/control/restart` | `systemctl restart nginx` (host agent or local) |
| GET | `/api/config/files` | Editable files |
| GET · PUT | `/api/config/file/<path>` | Read / write a config file |
| GET | `/api/sites` | Site list (enabled + available) |
| GET · PUT · DELETE | `/api/site/<name>` | Site CRUD |
| POST | `/api/site` | Create reverse-proxy site |
| POST | `/api/site/<name>/toggle` | Enable / disable site |
| GET | `/api/ssl` | List saved certificates |
| POST | `/api/ssl` | Add a certificate (paths or PEM) |
| PUT · DELETE | `/api/ssl/<name>` | Update / delete a certificate |
| GET | `/api/logs?lines=&query=` | Error/Access log tail |
| GET | `/api/backup` | Config tarball (base64) |
| POST | `/api/restore` | Apply + `nginx -t` + rollback |
| GET | `/healthz` `/readyz` | Probes (open) |
| GET | `/metrics` | Prometheus metrics (auth-gated) |
| GET | `/api/metrics/portal` | Gauges as JSON |
---
## Development
```bash
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt -r requirements-dev.txt
pytest # full suite, no nginx required (binary faked)
```
The suite covers status + status-source priority, config editing (`nginx.conf`,
sites, `.env`), reverse-proxy site create/edit with separate HTTP/HTTPS ports,
SSL store with both file-path and pasted-PEM modes, backup/restore, the control
agent path, auth + lockout, and the API. CI runs on GitHub Actions for every
push and PR.
---
## Project structure
```
nginx-webui/
├── app.py # Flask backend — JSON API only
├── nginx_manager.py # status, control, sites, SSL store, config, backup
├── host_agent.py # host-side nginx control agent (stdlib only)
├── metrics.py # /metrics, /healthz, /readyz (prometheus text)
├── templates/ # — (none: this service is API-only)
├── tests/ # pytest suite
├── requirements.txt # flask
├── Dockerfile # Container image
├── docker-compose.yml # Web UI container + host mounts + host.docker.internal
├── install.sh # bootstrapping one-shot installer (--check safe)
├── nginx-webui.service # Systemd unit (bare-metal)
├── nginx-webui-host-agent.service # Systemd unit for the host control agent
└── .env.example # Container configuration template
```
> The web-UI frontend lives in the companion repo
> [himalsimkhada/webui](https://github.com/himalsimkhada/webui) — the admin
> facade that talks to nginx-webui (and BIND) over HTTP.
---
## How it works
The backend drives nginx through the exact same tools an admin does:
- **Control** — `nginx -t`, `nginx -s reload`, `systemctl restart nginx`;
delegated to the **host control agent** when running in a container
(PID namespaces block direct signals).
- **Status** — host agent `/status`, an HTTP `stub_status` probe, or local
`/proc` + `nginx -v` / `nginx -T` on bare metal; the dashboard shows which.
- **Config files** — reads/writes `nginx.conf`, `conf.d/`, `snippets/`,
`sites-available/` under `/etc/nginx` (or your `NGINX_CONF_DIR`).
- **Sites** — generated server blocks include TLS (`server_name`, `listen 443 ssl`,
cert/key), optional HTTP→HTTPS redirect, websocket `Upgrade` headers, and
`client_max_body_size` / `proxy_read_timeout`.
- **Validation** — every saved server block passes `nginx -t` before the
reload is offered.
- **Logs** — mounted log files (containers) or journalctl (bare metal).
Transport is chosen from the environment: as a systemd service it acts directly
on the host nginx; inside a container it talks to the host through
`NGINX_CTL_URL`. No database. No magic.
---
<div align="center">
**Poke around, file an issue, open a PR — feedback welcome.**
</div>
## License
MIT