From 9c1426d276227ea12c48b5e138ce5f4745e45875 Mon Sep 17 00:00:00 2001
From: marvin
Date: Fri, 18 Sep 2026 15:39:58 -0400
Subject: [PATCH] Add System tab; fix settings-revert bug, encryption-status
messaging, JS caching, native dropdown theming, and backup panel presentation
(v0.16.0)
---
README.md | 2 +-
ROADMAP.md | 17 +++++
package.json | 2 +-
src/public/features.js | 81 +++++++++++++++++++++--
src/public/index.html | 14 ++--
src/public/select-enhance.js | 121 +++++++++++++++++++++++++++++++++++
src/public/styles.css | 17 +++++
src/server.js | 64 +++++++++++++++++-
8 files changed, 303 insertions(+), 15 deletions(-)
create mode 100644 src/public/select-enhance.js
diff --git a/README.md b/README.md
index 1190741..72197b6 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-
+
Why Site Gateway ·
diff --git a/ROADMAP.md b/ROADMAP.md
index 9df37ff..c96f1da 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -34,6 +34,21 @@
- The Top Paths popout now states it's showing the top 10, matching the existing server-side cap.
- The Runtime/System dashboard panel's top accent bar changed from a stray `--blue` token to `--green`, matching the default accent already used by every other dashboard tile.
+`v0.15.2` fixed regressions introduced by `v0.15.1` and one deeper architectural bug:
+
+- The dashboard attention tile's inline "Resync now" button (added in `v0.15.1`) silently did nothing — a script-generation guard meant to avoid double-adding its click handler matched on markup text that had already been introduced by the same change, so the handler was never actually attached. Fixed and verified by checking for the handler's functional code rather than just a string match.
+- The Needs Attention dashboard chip and the attention-tile detail rows used different colors (amber vs. red) for the same condition; aligned to red.
+- Configuration drift kept re-reporting immediately after a successful resync. The `v0.15.1` fix (order-independent JSON comparison) was necessary but not sufficient — the deeper issue was comparing a live running config against a freshly re-adapted Caddyfile, which will almost never match because Caddy fills in runtime defaults (automation policy, TLS management state) that never appear in a bare adapted config. Rewrote drift detection to compare two live-config snapshots against a captured baseline instead, recapturing that baseline after every successful sync.
+- Removed the redundant "Resync now" callout from the Gateway Defaults page, superseded by the dashboard's inline button.
+
+`v0.16.0` adds the System tab and closes out a round of fixes found during live use of `v0.15.x`:
+
+- **New System tab** (Administration, first tab) — a read-only operations/diagnostics page: environment and integration status (Docker socket, `BACKUP_PASSWORD`), security status (default-credential and `ACME_EMAIL` detection), persistent gateway sync status with a Resync control, a scheduled-jobs table, per-folder storage usage, version/runtime info, and Reload/Restart controls. Restart is only enabled when the Docker socket is mounted and the container's own restart policy (checked via the Docker Engine API) is `always`, `unless-stopped`, or `on-failure`. The only interactive elements on the page are the Docker container-picker toggle (moved here from Gateway Defaults, which no longer carries integration/environment content) and the action buttons — everything else is status.
+- Fixed a real correctness bug: `PATCH /api/settings` called `syncCaddy()` unconditionally before saving anything, for every settings change — including backups, certificate-health, and log-retention changes that have nothing to do with the Caddy config. An unrelated Caddy resync failure could silently discard and revert a just-saved change before it was ever persisted. `syncCaddy()` now only runs when a `defaultSite` change is part of the request; everything else saves unconditionally.
+- The "Encrypt scheduled backups" toggle's helper text now positively confirms when `BACKUP_PASSWORD` is configured, instead of showing the same generic instructional copy regardless of whether it's set.
+- `app.js`/`features.js`/`select-enhance.js` are now served with `Cache-Control: no-cache`, so browsers always revalidate instead of potentially serving a stale cached copy despite the version query string.
+- Native `` popups across the app are now replaced with a custom-drawn dark-themed listbox (the underlying native select is kept for form/value/event compatibility) — the `color-scheme` CSS hint shipped in `v0.15.1` turned out not to reliably theme native dropdown popups across real browsers/engines.
+
## Product direction
Site Gateway stays simpler than a general-purpose proxy manager: one dashboard, clear health reporting, and guided setup instead of exposing raw server configuration. **Caddy** remains the managed gateway — Site Gateway stores a small route model and generates/validates Caddy configuration rather than reimplementing certificate and proxy behavior itself.
@@ -83,6 +98,8 @@ Site Gateway stays simpler than a general-purpose proxy manager: one dashboard,
- A durable, database-backed history of every backup, restore, and deletion attempt, shown as a human-readable timeline.
- An opt-in Docker container picker (gated on the Docker socket being mounted and readable) for choosing Proxy/Streaming targets from the host’s running containers instead of typing them by hand.
+- A System tab (Administration) surfacing environment/integration status, security status, storage usage, scheduled jobs, gateway sync status, and reload/restart controls in one read-only operations page.
+
### Brand and docs
- Current icon and wordmark (v0.11.99) used consistently across the login screen, sidebar, themed default pages, and this README.
diff --git a/package.json b/package.json
index 8bd49b4..d93cc7d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "site-gateway",
- "version": "0.15.2",
+ "version": "0.16.0",
"private": true,
"description": "Site Gateway: simple self-hosted website publishing, reverse proxying, and automatic HTTPS.",
"type": "module",
diff --git a/src/public/features.js b/src/public/features.js
index 957b2fe..15c1ab4 100644
--- a/src/public/features.js
+++ b/src/public/features.js
@@ -79,7 +79,7 @@ function renderBackups() {
const completeBackups = state.backups.filter(item => item.type === "complete"), configBackups = state.backups.filter(item => item.type === "configuration");
const summaryEl = document.querySelector("#backup-summary");
if (summaryEl) summaryEl.textContent = state.backups.length ? `${completeBackups.length} Complete (${formatBytes(completeBackups.reduce((sum, item) => sum + item.size, 0))}), ${configBackups.length} Configuration only (${formatBytes(configBackups.reduce((sum, item) => sum + item.size, 0))}).` : "";
- document.querySelector("#backup-list").innerHTML = state.backups.length ? state.backups.map(item => `${extendedEscape(item.filename)} ${formatTime(item.createdAt)}
${backupTypeLabel(item.type)} Site Gateway ${extendedEscape(item.appVersion)}
${formatBytes(item.size)} ${item.valid ? "Verified manifest" : "Unreadable manifest"}
`).join("") : 'No stored backups yet.
';
+ document.querySelector("#backup-list").innerHTML = state.backups.length ? state.backups.map(item => `${backupTypeLabel(item.type)} backup — ${formatTime(item.createdAt)} ${formatBytes(item.size)}
${backupTypeLabel(item.type)} Site Gateway ${extendedEscape(item.appVersion)}
${item.valid ? "Verified" : "Unreadable"} ${item.valid ? "Manifest checks out" : "Manifest could not be read"}
`).join("") : 'No stored backups yet.
';
}
// Toggle the "configuration only" warning banner whenever scheduling or the
// backup type changes.
@@ -275,7 +275,7 @@ document.addEventListener("click", event => { if (event.target.closest(".create-
function decorateAccessToggles() { document.querySelectorAll("#access-list [data-access-id]").forEach(card => { const item = state.accessLists.find(value => value.id === card.dataset.accessId); const footer = card.querySelector(".card-footer"); if (!footer || !item) return; card.querySelectorAll(".menu [data-access-action=toggle]").forEach(button => button.remove()); if (footer.querySelector("[data-access-action=toggle]")) return; let actions = footer.querySelector(".card-actions"); if (!actions) { actions = document.createElement("div"); actions.className = "card-actions"; footer.append(actions); } const toggle = document.createElement("button"); toggle.className = "toggle " + (item.enabled !== false ? "on" : ""); toggle.dataset.accessAction = "toggle"; toggle.setAttribute("aria-label", (item.enabled !== false ? "Disable" : "Enable") + " Access List"); toggle.innerHTML = " "; actions.append(toggle); }); }
function decorateGroupCards() { document.querySelectorAll('[data-admin-panel="groups"] .group-card').forEach(card => { const group = state.groups.find(value => value.id === card.querySelector("[data-group-action]")?.dataset.groupId); if (!group) return; const icon = card.querySelector(".site-icon"); if (icon && icon.textContent.trim() === "GR") icon.innerHTML = featureIcon(group, "GR"); const menu = card.querySelector(".menu"); if (menu && !menu.querySelector("[data-group-action=icon]")) { const button = document.createElement("button"); button.dataset.groupAction = "icon"; button.dataset.groupId = group.id; button.textContent = "Change icon"; menu.prepend(button); } }); }
document.addEventListener("click", event => { const button = event.target.closest("[data-group-action=icon]"); if (!button) return; event.preventDefault(); event.stopImmediatePropagation(); openIconPicker("groups", button.dataset.groupId); }, true);
-function normalizeAdminTabOrder() { const tabs = document.querySelector(".admin-tabs"); if (!tabs) return; const order = ["users","groups","defaults","audit","backups","retention","api","danger"]; order.forEach((name, index) => { const button = tabs.querySelector(`[data-admin-tab="${name}"]`); if (button) { if (name === "retention") button.textContent = "Logs & Retention"; tabs.append(button); } }); }
+function normalizeAdminTabOrder() { const tabs = document.querySelector(".admin-tabs"); if (!tabs) return; const order = ["system","users","groups","defaults","audit","backups","retention","api","danger"]; order.forEach((name, index) => { const button = tabs.querySelector(`[data-admin-tab="${name}"]`); if (button) { if (name === "retention") button.textContent = "Logs & Retention"; tabs.append(button); } }); }
document.addEventListener("click", event => { if (event.target.closest(".admin-tabs")) setTimeout(normalizeAdminTabOrder, 0); });
// --- Backup encryption password field: placeholder/visibility polish -------------
@@ -290,7 +290,7 @@ function renderEncryptionToggle() {
const available = Boolean(state.config && state.config.backup && state.config.backup.encryptionAvailable);
const savedEncrypt = Boolean(state.settings && state.settings.backups && state.settings.backups.encrypt);
encryptionToggle.className = "encryption-toggle";
- let message = "Uses the container\u2019s BACKUP_PASSWORD value. Enable only after configuring that value.";
+ let message = "BACKUP_PASSWORD is configured \u2014 scheduled backups can be encrypted.";
if (!available && savedEncrypt) message = "This is enabled but BACKUP_PASSWORD is no longer configured \u2014 encrypted scheduled backups will fail until it\u2019s set again.";
else if (!available) message = "BACKUP_PASSWORD not configured \u2014 set it in the container\u2019s environment to enable encrypted scheduled backups.";
encryptionToggle.innerHTML = 'Encrypt scheduled backups ' + message + ' ';
@@ -446,7 +446,7 @@ async function renderBackupHistory() {
// saved value, so the integration can never be switched on without its prerequisite.
function renderDockerPanel() {
if (state.user?.role !== "administrator") return;
- const panel = document.querySelector('[data-admin-panel="defaults"]'); if (!panel) return;
+ const panel = document.querySelector('[data-admin-panel="system"] .system-integrations'); if (!panel) return;
const socketMounted = state.config?.docker?.socketMounted === true;
const enabled = socketMounted && (state.settings?.dockerIntegration?.enabled === true || state.config?.docker?.enabled === true);
let section = panel.querySelector(".docker-integration-section");
@@ -524,11 +524,84 @@ document.addEventListener("click", async event => {
});
+// --- System tab: environment/integration status, storage, scheduled jobs, sync, restart --------
+function renderSystemPanel() {
+ if (state.user?.role !== "administrator") return;
+ const tabs = document.querySelector(".admin-tabs"), users = document.querySelector('[data-admin-panel="users"]');
+ if (!tabs || !users) return;
+ let tab = tabs.querySelector('[data-admin-tab="system"]');
+ if (!tab) { tab = document.createElement("button"); tab.dataset.adminTab = "system"; tab.textContent = "System"; tabs.insertBefore(tab, tabs.firstChild); }
+ let panel = document.querySelector('[data-admin-panel="system"]');
+ if (!panel) { panel = document.createElement("section"); panel.dataset.adminPanel = "system"; panel.className = "settings-panel hidden"; users.parentElement.insertBefore(panel, users); }
+ if (!panel.dataset.ready) {
+ panel.dataset.ready = "1";
+ panel.innerHTML = [
+ 'System What\u2019s configured, what\u2019s running, and what this deployment can do. Nothing here is customizable except the Docker toggle below and the action buttons \u2014 everything else is status.
',
+ '',
+ 'Environment
Security status
',
+ '',
+ '',
+ '',
+ '',
+ 'Reloading re-applies the current configuration to Caddy with no downtime. Restarting stops and restarts the whole application \u2014 only available when a restart policy is set on the container.
Reload gateway config Restart application
',
+ ].join("");
+ panel.querySelector("#system-resync").addEventListener("click", async event => {
+ const button = event.currentTarget; button.disabled = true; const original = button.textContent; button.textContent = "Resyncing\u2026";
+ try { await api("/api/gateway/resync", { method: "POST" }); toast("Gateway configuration re-synced."); await refresh(); }
+ catch (error) { toast(error.message, "error"); }
+ finally { button.disabled = false; button.textContent = original; }
+ });
+ panel.querySelector("#system-reload").addEventListener("click", async event => {
+ const button = event.currentTarget; button.disabled = true; const original = button.textContent; button.textContent = "Reloading\u2026";
+ try { await api("/api/system/reload", { method: "POST" }); toast("Gateway configuration reloaded."); await refresh(); }
+ catch (error) { toast(error.message, "error"); }
+ finally { button.disabled = false; button.textContent = original; }
+ });
+ panel.querySelector("#system-restart").addEventListener("click", async event => {
+ if (!await themedConfirm("Restart Site Gateway?", "The application will stop and restart. This takes a few seconds and briefly interrupts hosted sites and the dashboard.", "Restart")) return;
+ const button = event.currentTarget; button.disabled = true; button.textContent = "Restarting\u2026";
+ try { await api("/api/system/restart", { method: "POST" }); toast("Restarting \u2014 this dashboard will be unavailable briefly."); }
+ catch (error) { toast(error.message, "error"); button.disabled = false; button.textContent = "Restart application"; }
+ });
+ }
+ renderSystemStatus(panel);
+}
+async function renderSystemStatus(panel) {
+ panel = panel || document.querySelector('[data-admin-panel="system"]');
+ if (!panel || panel.classList.contains("hidden")) return;
+ const security = document.querySelector("#system-security"), storage = document.querySelector("#system-storage"),
+ version = document.querySelector("#system-version"), jobs = document.querySelector("#system-jobs"),
+ syncStatus = document.querySelector("#system-sync-status"), restartButton = document.querySelector("#system-restart"),
+ restartStatus = document.querySelector("#system-restart-status");
+ if (jobs) jobs.innerHTML = (state.dashboard?.jobs || []).map(job => `${extendedEscape(job.name)} ${job.enabled ? `Active \u00b7 ${extendedEscape(job.schedule)}` : "Disabled"}
`).join("") || 'No scheduled jobs reported.
';
+ if (syncStatus) { const drift = (state.dashboard?.attention || []).some(item => item.kind === "drift"); syncStatus.textContent = drift ? "Configuration drift detected \u2014 the running gateway no longer matches the last known-good configuration." : `Gateway configuration is in sync. Last reload: ${state.dashboard?.gateway?.lastReload ? formatTime(state.dashboard.gateway.lastReload) : "unknown"}.`; syncStatus.className = drift ? "muted status-warning" : "muted"; }
+ if (version) version.innerHTML = `Site Gateway v${extendedEscape(state.config?.version || "unknown")} Data directory: ${extendedEscape(state.config?.storage?.databasePath ? state.config.storage.databasePath.replace(/\/database\/.*/, "") : "/data")} · Admin port: ${extendedEscape(String(state.config?.adminPort ?? ""))} · Site ports: ${extendedEscape(String(state.config?.minPort ?? ""))}\u2013${extendedEscape(String(state.config?.maxPort ?? ""))}`;
+ try {
+ const [sec, store, policy] = await Promise.all([
+ api("/api/system/security"),
+ api("/api/system/storage"),
+ api("/api/system/restart-policy"),
+ ]);
+ if (security) security.innerHTML = [
+ { ok: !sec.adminPasswordIsDefault, label: "ADMIN_PASSWORD", detail: sec.adminPasswordIsDefault ? "Still using the built-in default \u2014 set this before exposing the dashboard." : "Configured." },
+ { ok: !sec.sessionSecretIsDefault, label: "SESSION_SECRET", detail: sec.sessionSecretIsDefault ? "Not set \u2014 sessions are keyed off the admin credentials instead of an independent secret." : "Configured." },
+ { ok: sec.acmeEmailConfigured, label: "ACME_EMAIL", detail: sec.acmeEmailConfigured ? "Configured." : "Not set \u2014 certificate issuance will proceed without a registration contact." },
+ ].map(row => `${row.label} ${row.detail}
`).join("");
+ if (storage) {
+ const rows = Object.entries(store.breakdown || {}).map(([key, bytes]) => `${key[0].toUpperCase()}${key.slice(1)} ${formatBytes(bytes)}
`).join("");
+ const capacity = store.capacity ? `Disk ${formatBytes(store.capacity.availableBytes)} free of ${formatBytes(store.capacity.totalBytes)}
` : "";
+ storage.innerHTML = rows + capacity || 'Storage usage unavailable.
';
+ }
+ if (restartButton) { restartButton.disabled = !policy.restartAvailable; if (restartStatus) restartStatus.textContent = policy.reason || (policy.policyName ? `Restart policy: ${policy.policyName}.` : ""); }
+ } catch { /* Status widgets keep their last-known values if a refresh call fails. */ }
+}
+
// --- Wire the new panels into the shared refresh entry point ----------------------------------
const baseRenderExtendedViews = window.renderExtendedViews;
window.renderExtendedViews = function () {
baseRenderExtendedViews();
renderApiTokensPanel();
+ renderSystemPanel();
renderDockerPanel();
decorateContainerPickers();
renderBackupHistory();
diff --git a/src/public/index.html b/src/public/index.html
index 51abc88..5fb145e 100644
--- a/src/public/index.html
+++ b/src/public/index.html
@@ -8,7 +8,7 @@
Site Gateway
-
+
+
Users Groups Gateway defaults Audit log Backup & restore Logs & Retention Danger Zone
@@ -234,7 +236,7 @@
- Backup & restore Create a copy, restore a previous version, or schedule automatic backups.
Import backup Create backup
Storage guidance Backups are stored in /data/backups. Mount /backups separately to protect against appdata disk failure.
+ Backup & restore Create a copy, restore a previous version, or schedule automatic backups.
Import backup Create backup
Storage guidance Backups are stored in /data/backups. Mount /backups separately to protect against appdata disk failure.
On disk
Stored backups The actual backup files currently sitting in /data/backups. Download, Restore, and Delete here act on these files directly — deleting one here is permanent and removes it from disk, not just from this list.
Danger Zone These actions can permanently remove Site Gateway data. Review each warning carefully before continuing.
Restore defaults
Reset gateway preferences Restore default site behavior, backup scheduling, certificate thresholds, and interface preferences. Your users, routes, certificates, logs, and backups remain intact.
Restore default settings Permanent action
Factory reset Deletes all Site Gateway data under /data, including users, routes, certificates, logs, backups, and settings. Docker-mounted files outside /data are not affected. The container restarts at first-install setup.
@@ -313,7 +315,7 @@
- Site Gateway manual
Every section, every field, explained A complete reference for the Dashboard, Hosted Sites, Proxy Hosts, Redirect Hosts, Streaming Hosts, Certificates, Access Lists, My Account & two-factor authentication, Administration, Performance monitoring, and every advanced control between them. Start with an area’s overview, then use its field reference when you need to know what one specific setting does.
Search the complete manual Searches every heading, field name, and explanation on this page.
Contents
Introduction Getting started Dashboard Hosted Sites Proxy Hosts Redirect Hosts Streaming Hosts Certificates Access Lists Users & Groups My Account Gateway Defaults Backup & Restore Logs & Retention Danger Zone Logs Performance Common Controls Icons Troubleshooting Introduction
Why Site Gateway exists Reverse proxies often expose powerful settings without explaining what they change. Site Gateway provides a visual, Caddy-powered control plane for static sites, proxy routes, redirects, raw TCP/UDP streams, HTTPS, health checks, access control, and recovery — covering everything from a single ZIP upload to a full homelab of proxied applications.
How this manual is organized Each area below has an overview article (what it’s for and how to configure the common case) followed by a field reference article covering every advanced control, one at a time. Use the sidebar to jump straight to a section, or search for any field name, error message, or setting — search matches headings, body text, and field names together.
Novice path Create one route, test it locally, then add a domain and TLS. Keep defaults until you have a reason to change them.
Expert note Configuration is stored in SQLite under /data and every generated Caddy configuration is validated before reload — if validation fails, the previous working configuration stays active and the failure is explained in Gateway Events.
Getting started
From installation to your first route Install the container with persistent /data storage, open the management port (8080 by default), and complete the administrator setup screen shown on first launch. Before publishing public domains, confirm DNS already points to this server and that ports 80 and 443 are free for Site Gateway to use.
Choosing your first route type Hosted Site: you have static files (a ZIP or a single index.html) and want Site Gateway to serve them directly.Proxy Host: you already have an application running somewhere — another container, a LAN device, another server — and want a domain and HTTPS in front of it.Redirect Host: you want a domain to simply forward visitors to a different address.Streaming Host: you need to forward a raw TCP or UDP port (SSH, Minecraft, a game server) with no domain and no HTTPS involved.Example Publish a ZIP on a direct port first so you can confirm it works over LAN, then add www.example.com once DNS and port forwarding are ready and Automatic HTTPS can issue a certificate.
Dashboard
What each panel is telling you The Dashboard is a glance-and-go summary — every number and status on it links back to a full section elsewhere, so you never have to act on the Dashboard itself.
Metric strip Counts of Hosted Sites, Proxy Hosts, and Certificates needing attention, plus a Throughput chip showing live requests in the last minute across every configured domain — click it to open the full Performance tab.
Live Health Reverse proxy / Certificates / Persistent storage: the original health tiles.Streaming ports: how many enabled Streaming Hosts are actually bound and listening, versus configured.Upstreams: how many Proxy Hosts are currently passing their health check.Runtime & System Container and version facts, plus a Public IP tile showing your gateway's current public address, rechecked automatically every 60 minutes — prep work for future Dynamic DNS support. This is a status fact, not a job you configure.
Scheduled Jobs Background jobs shown as status tiles — proxy health checks, backups, log pruning, certificate/domain readiness checks, and the Public IP check — each showing its schedule and last result.
Needs Attention Lists anything that needs a decision (a failing health check, an expiring certificate). Clicking an item jumps straight to it. The Configuration drift item is the exception — it has its own inline Resync now button so you can clear it without leaving the Dashboard. When nothing needs attention, the panel collapses to a single all-clear banner instead of an empty list.
Recent Activity The last several configuration and operational events, with relative timestamps (“4 hours ago”). This is a shortcut, not a separate log — the same events are searchable in full under Logs → Gateway Events.
Hosted Sites
Overview: publish a static website A Hosted Site serves files you upload directly — no separate application or container required. Use it for a static site, a single-page app build, documentation, or anything that’s just HTML/CSS/JS.
Creating a Hosted Site Name: the label shown in Site Gateway; it doesn’t need to match the domain.Primary domain Optional : leave blank for port-only LAN access, or set a hostname to serve the same files there once DNS is ready.Additional domains: one alias per line — every alias serves the same files under the same TLS settings as the primary domain.Port: the direct LAN port this site answers on. Must fall inside the configured port range (shown as a hint on the form, default 9000–9099) and not already be used by another site.TLS: Automatic public HTTPS (default, requires a working domain and open ports 80/443), Internal HTTPS for trusted local devices , or HTTP only .Enable HSTS after HTTPS is verified: only turn this on once you’ve confirmed HTTPS works for every client — HSTS tells browsers to refuse HTTP entirely for this domain going forward, and that’s hard to undo quickly.Website files: upload a ZIP whose root contains index.html (a single top-level folder inside the ZIP is automatically flattened), or upload a bare index.html file directly. Up to 250 MB.Updating an existing site Use Replace files from the card’s menu to upload a new ZIP or HTML file without recreating the site or losing its domain/TLS/health settings. Use Domain & TLS to change the domain, TLS mode, or any advanced setting below.
Expected behavior Files are served immediately on the chosen port and, once configured, through the domain as well.
Hosted Sites
Advanced options and health monitoring Expand Advanced options on the create or edit form for these controls:
Access List: applies a reusable network or login restriction before any visitor reaches this site. Choose Public — no Access List to leave it open.Compression: Automatic zstd + gzip (default), gzip only , or Off .Request headers / Response headers: one Name: value pair per line, up to 30 each. Request headers are added before Caddy processes the request; response headers are added to what visitors receive.Apply HSTS to subdomains: adds includeSubDomains to the HSTS header; only takes effect when HSTS itself is on and TLS isn’t HTTP only.Custom Caddy configuration: expert-only raw Caddyfile lines appended to this site’s block, validated before reload. Lines that would touch the global config block, the admin API, storage, or import/persist_config directives are rejected outright, since those could affect every other route on the gateway.Monitoring this site Hosted Sites can be health-checked the same way Proxy Hosts are: a Health-check path (default /), method (GET or HEAD), expected status (a code, list, or range such as 200, 200,204, or 200-499), a timeout in seconds (1–60, default 4), up to 3 retries , and a Monitor this site toggle. Turning monitoring off shows the card as “Monitoring paused” instead of running a periodic check.
Proxy Hosts
Overview: connect a local application A Proxy Host puts a domain and HTTPS in front of something already running elsewhere — another container, a LAN device, or a remote service.
Creating a Proxy Host Name: the label shown in Site Gateway.Primary domain and Additional domains: same behavior as Hosted Sites.Forward to: the upstream address, e.g. http://192.168.1.20:8123. Use the container name, LAN address, or application URL — no path or query string beyond a trailing slash.Upstream pool Optional : found under Advanced options — additional http(s):// targets, one per line, up to 10. When present, Caddy distributes requests across every healthy target instead of using the single Forward-to address alone, and the Advanced options’ Load balancing setting controls how.TLS: Automatic public HTTPS, Internal HTTPS, Custom uploaded certificate , or HTTP only.Enable HSTS after HTTPS is verified. Example For Jellyfin at 192.168.1.20:8096, use domain jellyfin.example.com and forward target http://192.168.1.20:8096. Site Gateway checks the upstream continuously and Caddy manages an eligible public HTTPS certificate automatically.
Proxy Hosts
Advanced options, field by field Most applications only need a domain, Forward-to target, and TLS choice. These controls are for applications with unusual paths, authentication needs, response codes, headers, or performance requirements.
Access List: applies reusable login and network rules before the upstream is reached.Health-check path and method: the request Site Gateway makes when checking this application (path default /; GET or HEAD).Expected status: accepts a single code, a comma list, or a range — 200, 200,204, or 200-399.Timeout in seconds: 1–60, default 4.Monitor this upstream: when off, the status shows “Monitoring paused” and no periodic request is made.Compression: Automatic zstd + gzip, gzip only, or Off.Block common exploits: when enabled, Site Gateway checks the request’s URL path against one fixed, built-in pattern and responds 403 before the request ever reaches the upstream if it matches. The pattern currently checks the path for: path traversal sequences (../ or ..\); direct requests for /etc/passwd; WordPress probing (/wp-login.php, /wp-admin/, /xmlrpc.php); exposed config/VCS paths (/.env, /.git/, /.aws/); exposed PHPUnit test endpoints (/vendor/phpunit, /phpunit/); the literal strings eval( and base64_decode(; a SQL UNION SELECT sequence; and <script. Matching is case-insensitive. Important: this only inspects the URL path — it does not inspect the query string, request body, cookies, or headers, so a SQL-injection or XSS payload sent as a query parameter (e.g. ?id=1 UNION SELECT...) is not caught by this toggle. This is a small, fixed, path-only ruleset, not a full web application firewall — it will not catch every attack, cannot currently be extended with custom patterns, and should not replace keeping the upstream application itself patched.Custom Locations: send specific paths to a different upstream. One per line: /api/* | http://192.168.1.20:3001 | strip or preserve — strip removes the matched prefix before forwarding, preserve keeps it. Up to 20 entries.Request headers / Response headers: same Name: value-per-line format as Hosted Sites.Upstream TLS server name: an optional SNI name expected by the upstream certificate. Only meaningful when every upstream target uses https:// — the field is disabled otherwise.Ignore upstream TLS certificate errors: use only for a trusted internal HTTPS service with a self-signed or hostname-mismatched certificate; also requires an HTTPS upstream to take effect.Apply HSTS to subdomains. Custom Caddy configuration: same validation rules as Hosted Sites’ custom configuration. NGINX syntax is not supported — Site Gateway generates Caddyfile syntax.Upstream pool: additional http(s):// targets, one per line, up to 10 — the same field described in the Overview, now editable here too on an existing Proxy Host, not just when first creating it. Once Upstream pool has any entries, Caddy uses only those targets — the single Forward-to address above is not automatically added to the pool alongside them, so include it as one of the pool lines too if it should keep receiving traffic.Load balancing: only takes effect once Upstream pool has 2 or more entries — Forward to is never counted toward this, per the note above. Random (Caddy’s own default) picks a target at random per request; Round Robin rotates through targets evenly; Least Connections sends each request to whichever target currently has the fewest active requests; IP Hash sends a given client’s requests to the same target each time (sticky sessions), useful for applications that keep session state on one backend. This only affects which upstream Caddy sends a request to — it does not change the separate Health-check settings below, which only drive Site Gateway’s own dashboard status.How to verify a change Save one setting at a time, watch the card’s upstream status line, check Access Logs, and compare against a direct request to the application. If Caddy rejects a custom configuration, Site Gateway keeps the last known-good configuration active and shows the rejection reason.
Proxy Hosts
Custom uploaded certificates Choosing Custom uploaded certificate as the TLS mode reveals two file fields: Certificate PEM and Private key PEM . Both are required together — Site Gateway verifies the private key’s public key actually matches the certificate before accepting the pair, and confirms the certificate’s Subject Alternative Names cover every domain configured on that Proxy Host. A certificate that doesn’t cover every alias is rejected rather than silently applied to only some of them.
Accepted files are stored under /data/certificates/custom and included in complete backups. Replacing a custom certificate later works the same way — upload both files again through the same field.
Redirect Hosts
Overview: move an address safely A Redirect Host sends visitors from one domain straight to another, with no files hosted and no application behind it.
Fields Name. Source domain: the old address people currently use.Additional source domains: further aliases that redirect the same way.Destination: a complete http:// or https:// URL to send visitors to.Redirect type: 302 Temporary (default), 301 Permanent, 307 Temporary — preserve method, 308 Permanent — preserve method.TLS: Automatic HTTPS, HTTP only, or Internal HTTPS — this governs the source domain, not the destination.Preserve path and query (on by default): when enabled, old.example.com/library?id=2 becomes new.example.com/library?id=2 instead of always landing on the destination’s root.Access List Optional : protects the source domain the same way it protects Hosted Sites and Proxy Hosts.Choosing a redirect type Use 301 or 308 only when the move is meant to be permanent — browsers and search engines cache these aggressively. Use 302 or 307 while you’re still testing the new destination.
Streaming Hosts
Overview: raw TCP/UDP port forwarding A Streaming Host forwards a raw TCP or UDP port straight to another host and port — there’s no domain, no HTTPS, and no HTTP layer involved at all, unlike every other route type in Site Gateway. Use it for services that speak their own protocol directly over a port: SSH, a Minecraft or other game server, a VPN endpoint, or any similar TCP/UDP service.
The one prerequisite: publish the port first Streaming Hosts listen for connections from inside the Site Gateway container itself — they are not routed through Caddy the way domains are. That means the incoming port must already be published on the container (added to your docker-compose.yaml or Unraid port mappings, with the container recreated) before you create the matching Streaming Host in the UI, or nothing outside the container will ever reach it.
Example docker-compose port mapping To forward SSH (22) and a Minecraft server (25565):
ports: - "22:22/tcp" - "25565:25565/tcp"
Creating a Streaming Host Name. Incoming port: 1–65535. Cannot be the admin port, 80, 443, a port already inside the Hosted Sites port range, or a port already used by another Streaming Host.Forward to: a plain host:port address, e.g. 192.168.1.20:25565. No http:// — this is a raw socket forward, not a web address.TCP / UDP: enable one or both. At least one must stay checked.Monitor this target: runs a TCP reachability probe against the forward address every check cycle, even for UDP-only streams (UDP itself has no reliable way to “ping” a service).Streaming Hosts
How the forwarding actually works TCP is a full one-to-one relay: every inbound connection gets its own fresh outbound connection to the forward target, and the two are piped together in both directions. Closing or erroring either side tears down the other.
UDP is connectionless, so Site Gateway multiplexes many remote clients over one listening port by tracking a short-lived “session” per unique client address, each with its own outbound socket to the forward target. An idle session is cleaned up automatically after about a minute of inactivity.
Health monitoring The monitor always performs a plain TCP reachability check against the forward address, with a 4-second timeout — this confirms the target host and port are reachable, not that the specific game or service protocol is fully healthy. Disabling monitoring shows the card as “Monitoring paused” rather than unreachable.
Practical examples SSH: incoming port 2222, forward to 192.168.1.30:22, TCP only.Minecraft: incoming port 25565, forward to 192.168.1.20:25565, TCP (and UDP if the specific server/mod needs it — vanilla Minecraft is TCP-only).Editing a Streaming Host Changing the port, target, or protocol checkboxes restarts the listener immediately to apply the change; toggling Enable/Disable does the same.
Certificates
Overview: automatic HTTPS prerequisites For Automatic HTTPS to succeed, a domain must resolve to your public address, inbound ports 80 and 443 must reach Site Gateway, and no other service (including another reverse proxy) can already own those ports on this host.
Domain readiness checks DNS: confirms the domain actually resolves, and to what address.HTTP / HTTPS: confirms Caddy itself is listening on ports 80/443 inside the container — this does not by itself prove the internet can reach you, only that the gateway is ready to answer if it can.TLS: reflects the certificate status for that domain (healthy, renewing soon, critical, expired, or “Waiting for Caddy,” meaning issuance hasn’t completed yet — not that anything is broken).Upstream (Proxy Hosts only): the latest health-check result for that route.Check now Forces an immediate re-check of every certificate, domain readiness result, and health probe instead of waiting for the periodic background check.
Novice workflow Confirm DNS, forward ports 80 and 443, stop any competing proxy, then run Check now. Don’t troubleshoot an upstream application until the domain and HTTPS checks themselves are healthy.
Certificate health thresholds See Certificates → Field reference for what Renewing-soon warning, Critical warning, and Stale health data actually control.
Certificates
Field reference and thresholds Expanding a certificate’s details shows: status, valid-from date, issuer, every domain it covers, serial number, SHA-256 fingerprint, and when it was last detected — deliberately excluding private key material.
Status thresholds Set from Administration → Security & Health:
Renewing-soon warning: days remaining before a certificate is flagged as renewing soon (8–120, default 30).Critical warning: days remaining before it’s flagged critical; must stay lower than the renewing-soon threshold (default 7).Stale health data: minutes before a displayed check is considered old and worth re-running (2–1440, default 10).A status of mismatch means a custom certificate was uploaded for that route but its coverage doesn’t include every configured domain — upload a replacement covering all of them.
Access Lists
Overview: protect a route An Access List restricts who can reach a Hosted Site, Proxy Host, or Redirect Host, by network address, by login, or both. Assign a saved list from that route’s Advanced options.
Fields Name. Allowed networks: one IP, CIDR range, or the literal private_ranges per line. When set, every network not listed is denied — this is an allow-list, not a suggestion.Denied networks Optional : evaluated before allowed networks and logins, so a denied entry always wins even if it would otherwise be allowed.Logins: add one or more username/password pairs directly on the Access List (passwords need at least 8 characters; leaving a password blank while editing an existing login keeps it unchanged).Groups: alternatively, let members of a Group (Administration → Users & Groups) authenticate with their own Site Gateway username and password instead of a separate Access-List-only login. A disabled Group (Administration → Users & Groups) stops granting access immediately, even though it still appears assigned here — see Users & Groups for details.At least one rule — a network rule, a denied-network rule, or a login — is required before the list can be saved.
Assigning and inspecting Use a route’s Advanced options to assign an Access List to it, or use the Access List’s own View assigned hosts menu action to see everywhere it’s currently in use. An Access List can’t be deleted while anything still references it — unassign it from every host first.
Users & Groups
Overview: control who can change the gateway Every account has a role:
Administrator: full access, including Users, Groups, Settings, and Backups.Standard User: can manage Hosted Sites, Proxy Hosts, Redirect Hosts, Streaming Hosts, and Access Lists, but not Users, Groups, Settings, or Backups.Viewer: read-only — can inspect everything but change nothing.Creating a user Display name. Username: 3–64 characters, letters/numbers/._-, must be unique.Role. Temporary password: at least 8 characters — share it securely and have the person change it after their first sign-in.Managing an existing user An administrator can change a user’s role from the card’s role dropdown, reset their password, archive them (a reversible soft-disable distinct from deleting), or delete them outright. Delete and Change icon live under the card’s “•••” menu, matching Hosted Sites and Proxy Hosts; role, password reset, and archive stay as visible buttons. You cannot disable, archive, delete, or change the role of your own account — another administrator has to do that — and Site Gateway always keeps at least one active Administrator, refusing any action that would leave zero. Resetting a user’s password here does not disable their two-factor authentication if they have it enabled — see My Account for how 2FA works. If a user is locked out of their own 2FA (lost authenticator, no recovery codes left), an administrator can disable it for them from the card’s “•••” menu — Disable 2FA appears there only when that user currently has 2FA enabled. This clears their authenticator, secret, and any unused recovery codes; they can set 2FA up again afterward if they choose to. The action is logged to the Audit log, since it removes a security control from someone else’s account.
Groups A Group is simply a named set of users. Its only purpose is authentication: assign a Group to an Access List so its members can log in with their own Site Gateway credentials instead of a separate Access-List-only login.
Every Group has its own enable/disable toggle, separate from deleting it. Disabling a Group immediately stops it from granting access through any Access List it’s assigned to — the assignment itself is untouched and still shows as assigned, but its members can no longer authenticate through it until the Group is re-enabled. This is a common surprise: disabling a Group is not the same as archiving it for later cleanup, it has an immediate access-control effect. If the Group is currently assigned to at least one enabled Access List, disabling it now asks for confirmation first, naming exactly which Access List(s) will stop authenticating that Group’s members — the same pattern already used when disabling an Access List that protects active hosts.
Audit history Administration includes an Audit log — a searchable, filterable, immutable record of who did what, filterable by outcome (success/failed) and free-text search across the user, action, and target. Audit entries can’t be edited or deleted.
My Account
Managing your own profile, password, and two-factor authentication My Account is available to every role — Administrator, Standard User, and Viewer alike — and only ever affects your own account. It sits in the sidebar next to Administration and Documentation.
Profile Shows your display name, username, and role. These are read-only here; an administrator changes them from Administration → Users.
Changing your password Enter your current password once, then your new password twice. This works for every role — previously, only an administrator could change a user’s password (Administration → Users → Reset password), which meant every routine password change had to go through an admin. My Account closes that gap for everyone’s own account.
Two-factor authentication (2FA) Optional and off by default for every account. When enabled, signing in requires your password plus a 6-digit code from an authenticator app (or a recovery code), using the standard TOTP algorithm (RFC 6238) — any authenticator app works, not a Site Gateway-specific one.
Enabling 2FA Choose Enable two-factor authentication. Scan the QR code with your authenticator app, or enter the shown key manually. Enter the 6-digit code it generates to confirm setup. Save the 10 recovery codes shown immediately afterward — each works once, and this is the only time they’re shown in full. Signing in with 2FA enabled After your username and password are accepted, you’re prompted for a code. Enter the current 6-digit code from your authenticator app, or one of your unused recovery codes if you don’t have the app available. A wrong code shows an inline error without sending you back to the username/password screen.
Disabling 2FA, or regenerating recovery codes Both require re-entering your current password as confirmation. Regenerating recovery codes immediately invalidates the previous set.
An administrator resetting your password does not disable your 2FA — you’ll still need your authenticator app or a recovery code at your next sign-in. Only you can turn off your own 2FA, from My Account.
Administration
Gateway Defaults: handling unknown addresses Controls what happens when a visitor reaches Site Gateway on HTTP using a hostname that isn’t configured. (Unknown HTTPS hostnames are always rejected outright, regardless of this setting, since serving anything else would need a certificate Site Gateway doesn’t have — and issuing a misleading one would be worse.)
Response modes Themed route-not-found page (404) — the safest public default.Gateway ready page (200) — useful while confirming HTTP routing during initial setup.No response — close connection. Redirect elsewhere: set a destination URL, redirect code, and whether to preserve the requested path and query.Custom HTML: administrator-authored markup, served exactly as written with no sanitization — up to 250,000 characters.The themed page’s heading and explanation text are also editable here, independent of which mode is active. It displays the Site Gateway icon and wordmark, matching the branding used throughout the rest of the app.
Live preview Configuration drift detection Every 10 minutes (and once shortly after startup), Site Gateway compares Caddy’s live running configuration against what your saved routes would currently generate. If they disagree — for example after a manual edit outside the app, or a Caddy restart that didn’t pick up the latest reload — a “Configuration drift” item appears on the Dashboard’s Needs Attention list with its own inline “Resync now” button, which simply re-runs the normal save-and-reload path and clears the flag; it does not change any of your saved settings. The first time drift is detected, it’s also recorded as a Gateway Events entry.
Administration
Backup & Restore A Configuration only backup contains a consistent SQLite snapshot of every route, user, group, Access List, and setting, plus a portable JSON export of the same data. A Complete backup adds uploaded Hosted Site files, icons, the default-site page, and certificates (both managed and custom).
Scheduled backups Enable scheduled backups. Type: Complete (the recommended default) or Configuration only. If scheduled backups are enabled while Configuration only is selected, a warning appears explaining that Hosted Site files, icons, and certificates won’t be included.Schedule: Daily, Weekly, or Monthly, plus the hour of day to run.Keep: how many scheduled backups to retain (1–100, default 7) — older ones beyond this count are deleted automatically after each run.Include logs. Encrypt scheduled backups: uses the container’s BACKUP_PASSWORD environment value. This checkbox is disabled with an explanation if BACKUP_PASSWORD isn’t currently set on the container — it can’t be turned on until that value exists. If it was previously enabled and BACKUP_PASSWORD was later removed from the container’s environment, it stays checked but shows a warning instead of failing silently at the next scheduled run — set the variable again (and restart the container) to resolve it.Manual backup, encryption password The optional backup password field on this page is used only for manually created backups and for restoring an encrypted archive — it is never stored by Site Gateway. A manually created backup is saved to /data/backups and appears in the list below; it does not download automatically — use that entry’s Download button when you want a local copy.
Restore checklist Download or import the .sgbackup archive (importing just stages the file — restoring is a separate, explicit action). Supply its password if it’s encrypted. Choose Restore and allow validation to finish. Confirm hosts, certificates, and upstream health afterward. Site Gateway verifies every file’s checksum and automatically creates a safety backup of the current state before restoring anything. If the restored configuration turns out to be invalid, it automatically rolls back to that safety backup rather than leaving the gateway in a broken state.
Configuration safety & updates Site Gateway validates every generated Caddy configuration before reload and keeps the previous working configuration active if validation fails. Container updates are installed by pulling a new pinned image — create a backup first.
Administration
Logs & Retention Set how many days of Access, Activity, Audit, Certificate, and Security records to keep (7–3650 days each) and whether automatic pruning is enabled. Prune Now shows exactly how many records in each category are eligible before you confirm, and Download Logs exports them.
Administration
Danger Zone Two separate, deliberately distinct destructive actions — kept apart so a routine preference correction is never confused with a full rebuild. Both require re-entering your own administrator username and password, typing an exact confirmation phrase, and then confirming a second themed dialog by typing YES .
Restore Defaults Confirmation phrase: RESTORE DEFAULT. Resets Gateway Defaults, backup schedule settings, and certificate-health thresholds back to their starting values. It does not remove any hosts, uploaded files, users, groups, Access Lists, certificates, logs, or backups.
Factory Reset Confirmation phrase: FACTORY RESET. Deletes everything under /data — every host of every kind, uploaded files, certificates (managed and custom), logs, backups, icons, users, and settings. Docker-mounted files outside /data are untouched. The container returns to the initial setup screen without needing a manual restart. Because backups themselves live under /data/backups, they’re deleted too — recovery is only possible from a backup taken beforehand and stored elsewhere (downloaded, or on separately mounted storage).
When to use a backup instead If you want to undo a recent change while keeping the rest of the installation intact, restore a backup — Factory Reset is not a rollback tool.
Logs
Access Logs and Gateway Events Access Logs Every request Caddy handles, with domain, path, response status, latency, and upstream outcome. Filter by host or by status-code range (2xx/3xx/4xx/5xx). Sensitive query-string values — tokens, secrets, passwords, session identifiers, API keys, credentials — are redacted before they’re ever stored, regardless of filter settings.
Gateway Events Configuration and operational changes, filterable by severity (Normal/Warnings/Errors) and by category (certificate, health, authentication, backup, configuration, or system).
Example Filter Access Logs for a 502, then compare the target address against a direct LAN request to the same upstream to isolate whether the problem is the gateway or the application itself.
Performance
Throughput by domain, over time Built entirely from the same request data already collected for Access Logs — no new logging or extra overhead, just a different view of it.
Filters Domain narrows the Trend chart to one host (default: all domains combined). Range sets the Trend window — Last hour, 3, 6 (default), 12, or 24 hours, or 3 or 7 days.
Trend Request volume over the selected range, bucketed into 15-minute (up to 24h) or hourly (3–7 days) points.
Per-route table One row per domain that has received traffic, sorted by 24-hour volume:
Last hour / Last 24h: request count for that window; a red count after the divider is how many of those responses were 4xx or 5xx. This includes routine noise (expired-token 401s, bots probing by raw IP, scanners hitting invalid hosts) as well as genuine failures — it isn’t a health verdict by itself.Avg. response: the mean response time across every request to that domain in the last 24 hours — a fixed 24h window regardless of the Range filter above, and a straight average, so a handful of slow outliers can pull it up more than most visitors actually experience.Top paths: a per-domain “View” popout listing the top 10 most-requested paths on that domain in the last 24 hours, with request counts.Common Interface Controls
Menus, toggles, and role gating The same card language is used throughout Hosted Sites, Proxy Hosts, Redirect Hosts, Streaming Hosts, Access Lists, Groups, and Users, so learning one area transfers directly to the next.
The three-dot menu Contains actions that change or inspect a card: Edit opens the full form, kind-specific extras appear where relevant (Replace files on Hosted Sites, View assigned hosts on Access Lists), and Delete removes the record after a confirmation.
The toggle switch A slide switch, separate from the menu, turns a route or account on or off without deleting its saved configuration — useful during maintenance or testing when you expect to reuse the exact same settings shortly after.
Who can use each control Administrators can manage everything. Standard Users can manage Hosted Sites, Proxy Hosts, Redirect Hosts, Streaming Hosts, and Access Lists, but not Users, Groups, Settings, or Backups. Viewers can inspect information but cannot create, edit, disable, assign, or delete anything — their menus and toggles are hidden entirely. This is enforced on the server independently of what the interface shows, so hiding a button is a convenience, not the actual security boundary.
Update notifications While signed in, Site Gateway checks every 60 seconds whether a newer version has been deployed. If so, a small banner appears with Refresh and Dismiss — Refresh reloads the page to pick up the new version; Dismiss hides the banner, but it reappears on the next check if you’re still on the old version. This only matters for a tab left open across a deploy; closing and reopening the tab, or signing in fresh, always loads the current version automatically.
Icons
Changing a card’s icon Every Hosted Site, Proxy Host, Redirect Host, Streaming Host, Access List, Group, and User can have its own icon. Open the picker from a card’s icon tile or its “Change icon” menu action.
Search by service name: type at least 2 characters (e.g. Jellyfin, Plex) to search a large built-in icon catalog and pick a match.Upload a custom image: PNG, JPEG, WebP, GIF, or SVG, up to 2 MB, stored locally under /data/icons. Uploaded SVGs are checked for embedded scripts or external references before being accepted.Image URL: paste a direct https:// image link instead of uploading a file.Use two-letter fallback: clears any icon and reverts to initials derived from the name.If a custom icon URL ever stops loading, the card automatically falls back to showing initials instead of a broken image.
Troubleshooting
When HTTPS is not detected Confirm public DNS actually points to this server, that router/firewall forwarding reaches ports 80 and 443, and that NGINX Proxy Manager or another reverse proxy isn’t still holding those ports. Then check Certificates and Logs → Gateway Events for the specific rejection reason. Site Gateway cannot request a public certificate while another gateway is receiving the ACME challenge on its behalf.
A route rejected its configuration Check the error shown on save — it names the specific problem (duplicate address, invalid upstream, malformed custom configuration, certificate/TLS issue) rather than a generic failure, and the previous working configuration stays active while you fix it.
A Streaming Host isn’t reachable from outside This is almost always the port not being published on the container yet — see Streaming Hosts → Overview for the docker-compose/Unraid port mapping requirement.
A user is locked out after enabling two-factor authentication If they still have an unused recovery code, they can sign in with it in place of the 6-digit code. If not, an administrator can disable that user’s 2FA from Administration → Users — open the locked-out user’s “•••” menu and choose Disable 2FA . The user can sign in with just their password afterward and set 2FA up again whenever they’re ready.
No guide matched that search.
+ Site Gateway manual
Every section, every field, explained A complete reference for the Dashboard, Hosted Sites, Proxy Hosts, Redirect Hosts, Streaming Hosts, Certificates, Access Lists, My Account & two-factor authentication, Administration, Performance monitoring, and every advanced control between them. Start with an area’s overview, then use its field reference when you need to know what one specific setting does.
Search the complete manual Searches every heading, field name, and explanation on this page.
Contents
Introduction Getting started Dashboard Hosted Sites Proxy Hosts Redirect Hosts Streaming Hosts Certificates Access Lists Users & Groups My Account System Gateway Defaults Backup & Restore Logs & Retention Danger Zone Logs Performance Common Controls Icons Troubleshooting Introduction
Why Site Gateway exists Reverse proxies often expose powerful settings without explaining what they change. Site Gateway provides a visual, Caddy-powered control plane for static sites, proxy routes, redirects, raw TCP/UDP streams, HTTPS, health checks, access control, and recovery — covering everything from a single ZIP upload to a full homelab of proxied applications.
How this manual is organized Each area below has an overview article (what it’s for and how to configure the common case) followed by a field reference article covering every advanced control, one at a time. Use the sidebar to jump straight to a section, or search for any field name, error message, or setting — search matches headings, body text, and field names together.
Novice path Create one route, test it locally, then add a domain and TLS. Keep defaults until you have a reason to change them.
Expert note Configuration is stored in SQLite under /data and every generated Caddy configuration is validated before reload — if validation fails, the previous working configuration stays active and the failure is explained in Gateway Events.
Getting started
From installation to your first route Install the container with persistent /data storage, open the management port (8080 by default), and complete the administrator setup screen shown on first launch. Before publishing public domains, confirm DNS already points to this server and that ports 80 and 443 are free for Site Gateway to use.
Choosing your first route type Hosted Site: you have static files (a ZIP or a single index.html) and want Site Gateway to serve them directly.Proxy Host: you already have an application running somewhere — another container, a LAN device, another server — and want a domain and HTTPS in front of it.Redirect Host: you want a domain to simply forward visitors to a different address.Streaming Host: you need to forward a raw TCP or UDP port (SSH, Minecraft, a game server) with no domain and no HTTPS involved.Example Publish a ZIP on a direct port first so you can confirm it works over LAN, then add www.example.com once DNS and port forwarding are ready and Automatic HTTPS can issue a certificate.
Dashboard
What each panel is telling you The Dashboard is a glance-and-go summary — every number and status on it links back to a full section elsewhere, so you never have to act on the Dashboard itself.
Metric strip Counts of Hosted Sites, Proxy Hosts, and Certificates needing attention, plus a Throughput chip showing live requests in the last minute across every configured domain — click it to open the full Performance tab.
Live Health Reverse proxy / Certificates / Persistent storage: the original health tiles.Streaming ports: how many enabled Streaming Hosts are actually bound and listening, versus configured.Upstreams: how many Proxy Hosts are currently passing their health check.Runtime & System Container and version facts, plus a Public IP tile showing your gateway's current public address, rechecked automatically every 60 minutes — prep work for future Dynamic DNS support. This is a status fact, not a job you configure.
Scheduled Jobs Background jobs shown as status tiles — proxy health checks, backups, log pruning, certificate/domain readiness checks, and the Public IP check — each showing its schedule and last result.
Needs Attention Lists anything that needs a decision (a failing health check, an expiring certificate). Clicking an item jumps straight to it. The Configuration drift item is the exception — it has its own inline Resync now button so you can clear it without leaving the Dashboard. When nothing needs attention, the panel collapses to a single all-clear banner instead of an empty list.
Recent Activity The last several configuration and operational events, with relative timestamps (“4 hours ago”). This is a shortcut, not a separate log — the same events are searchable in full under Logs → Gateway Events.
Hosted Sites
Overview: publish a static website A Hosted Site serves files you upload directly — no separate application or container required. Use it for a static site, a single-page app build, documentation, or anything that’s just HTML/CSS/JS.
Creating a Hosted Site Name: the label shown in Site Gateway; it doesn’t need to match the domain.Primary domain Optional : leave blank for port-only LAN access, or set a hostname to serve the same files there once DNS is ready.Additional domains: one alias per line — every alias serves the same files under the same TLS settings as the primary domain.Port: the direct LAN port this site answers on. Must fall inside the configured port range (shown as a hint on the form, default 9000–9099) and not already be used by another site.TLS: Automatic public HTTPS (default, requires a working domain and open ports 80/443), Internal HTTPS for trusted local devices , or HTTP only .Enable HSTS after HTTPS is verified: only turn this on once you’ve confirmed HTTPS works for every client — HSTS tells browsers to refuse HTTP entirely for this domain going forward, and that’s hard to undo quickly.Website files: upload a ZIP whose root contains index.html (a single top-level folder inside the ZIP is automatically flattened), or upload a bare index.html file directly. Up to 250 MB.Updating an existing site Use Replace files from the card’s menu to upload a new ZIP or HTML file without recreating the site or losing its domain/TLS/health settings. Use Domain & TLS to change the domain, TLS mode, or any advanced setting below.
Expected behavior Files are served immediately on the chosen port and, once configured, through the domain as well.
Hosted Sites
Advanced options and health monitoring Expand Advanced options on the create or edit form for these controls:
Access List: applies a reusable network or login restriction before any visitor reaches this site. Choose Public — no Access List to leave it open.Compression: Automatic zstd + gzip (default), gzip only , or Off .Request headers / Response headers: one Name: value pair per line, up to 30 each. Request headers are added before Caddy processes the request; response headers are added to what visitors receive.Apply HSTS to subdomains: adds includeSubDomains to the HSTS header; only takes effect when HSTS itself is on and TLS isn’t HTTP only.Custom Caddy configuration: expert-only raw Caddyfile lines appended to this site’s block, validated before reload. Lines that would touch the global config block, the admin API, storage, or import/persist_config directives are rejected outright, since those could affect every other route on the gateway.Monitoring this site Hosted Sites can be health-checked the same way Proxy Hosts are: a Health-check path (default /), method (GET or HEAD), expected status (a code, list, or range such as 200, 200,204, or 200-499), a timeout in seconds (1–60, default 4), up to 3 retries , and a Monitor this site toggle. Turning monitoring off shows the card as “Monitoring paused” instead of running a periodic check.
Proxy Hosts
Overview: connect a local application A Proxy Host puts a domain and HTTPS in front of something already running elsewhere — another container, a LAN device, or a remote service.
Creating a Proxy Host Name: the label shown in Site Gateway.Primary domain and Additional domains: same behavior as Hosted Sites.Forward to: the upstream address, e.g. http://192.168.1.20:8123. Use the container name, LAN address, or application URL — no path or query string beyond a trailing slash.Upstream pool Optional : found under Advanced options — additional http(s):// targets, one per line, up to 10. When present, Caddy distributes requests across every healthy target instead of using the single Forward-to address alone, and the Advanced options’ Load balancing setting controls how.TLS: Automatic public HTTPS, Internal HTTPS, Custom uploaded certificate , or HTTP only.Enable HSTS after HTTPS is verified. Example For Jellyfin at 192.168.1.20:8096, use domain jellyfin.example.com and forward target http://192.168.1.20:8096. Site Gateway checks the upstream continuously and Caddy manages an eligible public HTTPS certificate automatically.
Proxy Hosts
Advanced options, field by field Most applications only need a domain, Forward-to target, and TLS choice. These controls are for applications with unusual paths, authentication needs, response codes, headers, or performance requirements.
Access List: applies reusable login and network rules before the upstream is reached.Health-check path and method: the request Site Gateway makes when checking this application (path default /; GET or HEAD).Expected status: accepts a single code, a comma list, or a range — 200, 200,204, or 200-399.Timeout in seconds: 1–60, default 4.Monitor this upstream: when off, the status shows “Monitoring paused” and no periodic request is made.Compression: Automatic zstd + gzip, gzip only, or Off.Block common exploits: when enabled, Site Gateway checks the request’s URL path against one fixed, built-in pattern and responds 403 before the request ever reaches the upstream if it matches. The pattern currently checks the path for: path traversal sequences (../ or ..\); direct requests for /etc/passwd; WordPress probing (/wp-login.php, /wp-admin/, /xmlrpc.php); exposed config/VCS paths (/.env, /.git/, /.aws/); exposed PHPUnit test endpoints (/vendor/phpunit, /phpunit/); the literal strings eval( and base64_decode(; a SQL UNION SELECT sequence; and <script. Matching is case-insensitive. Important: this only inspects the URL path — it does not inspect the query string, request body, cookies, or headers, so a SQL-injection or XSS payload sent as a query parameter (e.g. ?id=1 UNION SELECT...) is not caught by this toggle. This is a small, fixed, path-only ruleset, not a full web application firewall — it will not catch every attack, cannot currently be extended with custom patterns, and should not replace keeping the upstream application itself patched.Custom Locations: send specific paths to a different upstream. One per line: /api/* | http://192.168.1.20:3001 | strip or preserve — strip removes the matched prefix before forwarding, preserve keeps it. Up to 20 entries.Request headers / Response headers: same Name: value-per-line format as Hosted Sites.Upstream TLS server name: an optional SNI name expected by the upstream certificate. Only meaningful when every upstream target uses https:// — the field is disabled otherwise.Ignore upstream TLS certificate errors: use only for a trusted internal HTTPS service with a self-signed or hostname-mismatched certificate; also requires an HTTPS upstream to take effect.Apply HSTS to subdomains. Custom Caddy configuration: same validation rules as Hosted Sites’ custom configuration. NGINX syntax is not supported — Site Gateway generates Caddyfile syntax.Upstream pool: additional http(s):// targets, one per line, up to 10 — the same field described in the Overview, now editable here too on an existing Proxy Host, not just when first creating it. Once Upstream pool has any entries, Caddy uses only those targets — the single Forward-to address above is not automatically added to the pool alongside them, so include it as one of the pool lines too if it should keep receiving traffic.Load balancing: only takes effect once Upstream pool has 2 or more entries — Forward to is never counted toward this, per the note above. Random (Caddy’s own default) picks a target at random per request; Round Robin rotates through targets evenly; Least Connections sends each request to whichever target currently has the fewest active requests; IP Hash sends a given client’s requests to the same target each time (sticky sessions), useful for applications that keep session state on one backend. This only affects which upstream Caddy sends a request to — it does not change the separate Health-check settings below, which only drive Site Gateway’s own dashboard status.How to verify a change Save one setting at a time, watch the card’s upstream status line, check Access Logs, and compare against a direct request to the application. If Caddy rejects a custom configuration, Site Gateway keeps the last known-good configuration active and shows the rejection reason.
Proxy Hosts
Custom uploaded certificates Choosing Custom uploaded certificate as the TLS mode reveals two file fields: Certificate PEM and Private key PEM . Both are required together — Site Gateway verifies the private key’s public key actually matches the certificate before accepting the pair, and confirms the certificate’s Subject Alternative Names cover every domain configured on that Proxy Host. A certificate that doesn’t cover every alias is rejected rather than silently applied to only some of them.
Accepted files are stored under /data/certificates/custom and included in complete backups. Replacing a custom certificate later works the same way — upload both files again through the same field.
Redirect Hosts
Overview: move an address safely A Redirect Host sends visitors from one domain straight to another, with no files hosted and no application behind it.
Fields Name. Source domain: the old address people currently use.Additional source domains: further aliases that redirect the same way.Destination: a complete http:// or https:// URL to send visitors to.Redirect type: 302 Temporary (default), 301 Permanent, 307 Temporary — preserve method, 308 Permanent — preserve method.TLS: Automatic HTTPS, HTTP only, or Internal HTTPS — this governs the source domain, not the destination.Preserve path and query (on by default): when enabled, old.example.com/library?id=2 becomes new.example.com/library?id=2 instead of always landing on the destination’s root.Access List Optional : protects the source domain the same way it protects Hosted Sites and Proxy Hosts.Choosing a redirect type Use 301 or 308 only when the move is meant to be permanent — browsers and search engines cache these aggressively. Use 302 or 307 while you’re still testing the new destination.
Streaming Hosts
Overview: raw TCP/UDP port forwarding A Streaming Host forwards a raw TCP or UDP port straight to another host and port — there’s no domain, no HTTPS, and no HTTP layer involved at all, unlike every other route type in Site Gateway. Use it for services that speak their own protocol directly over a port: SSH, a Minecraft or other game server, a VPN endpoint, or any similar TCP/UDP service.
The one prerequisite: publish the port first Streaming Hosts listen for connections from inside the Site Gateway container itself — they are not routed through Caddy the way domains are. That means the incoming port must already be published on the container (added to your docker-compose.yaml or Unraid port mappings, with the container recreated) before you create the matching Streaming Host in the UI, or nothing outside the container will ever reach it.
Example docker-compose port mapping To forward SSH (22) and a Minecraft server (25565):
ports: - "22:22/tcp" - "25565:25565/tcp"
Creating a Streaming Host Name. Incoming port: 1–65535. Cannot be the admin port, 80, 443, a port already inside the Hosted Sites port range, or a port already used by another Streaming Host.Forward to: a plain host:port address, e.g. 192.168.1.20:25565. No http:// — this is a raw socket forward, not a web address.TCP / UDP: enable one or both. At least one must stay checked.Monitor this target: runs a TCP reachability probe against the forward address every check cycle, even for UDP-only streams (UDP itself has no reliable way to “ping” a service).Streaming Hosts
How the forwarding actually works TCP is a full one-to-one relay: every inbound connection gets its own fresh outbound connection to the forward target, and the two are piped together in both directions. Closing or erroring either side tears down the other.
UDP is connectionless, so Site Gateway multiplexes many remote clients over one listening port by tracking a short-lived “session” per unique client address, each with its own outbound socket to the forward target. An idle session is cleaned up automatically after about a minute of inactivity.
Health monitoring The monitor always performs a plain TCP reachability check against the forward address, with a 4-second timeout — this confirms the target host and port are reachable, not that the specific game or service protocol is fully healthy. Disabling monitoring shows the card as “Monitoring paused” rather than unreachable.
Practical examples SSH: incoming port 2222, forward to 192.168.1.30:22, TCP only.Minecraft: incoming port 25565, forward to 192.168.1.20:25565, TCP (and UDP if the specific server/mod needs it — vanilla Minecraft is TCP-only).Editing a Streaming Host Changing the port, target, or protocol checkboxes restarts the listener immediately to apply the change; toggling Enable/Disable does the same.
Certificates
Overview: automatic HTTPS prerequisites For Automatic HTTPS to succeed, a domain must resolve to your public address, inbound ports 80 and 443 must reach Site Gateway, and no other service (including another reverse proxy) can already own those ports on this host.
Domain readiness checks DNS: confirms the domain actually resolves, and to what address.HTTP / HTTPS: confirms Caddy itself is listening on ports 80/443 inside the container — this does not by itself prove the internet can reach you, only that the gateway is ready to answer if it can.TLS: reflects the certificate status for that domain (healthy, renewing soon, critical, expired, or “Waiting for Caddy,” meaning issuance hasn’t completed yet — not that anything is broken).Upstream (Proxy Hosts only): the latest health-check result for that route.Check now Forces an immediate re-check of every certificate, domain readiness result, and health probe instead of waiting for the periodic background check.
Novice workflow Confirm DNS, forward ports 80 and 443, stop any competing proxy, then run Check now. Don’t troubleshoot an upstream application until the domain and HTTPS checks themselves are healthy.
Certificate health thresholds See Certificates → Field reference for what Renewing-soon warning, Critical warning, and Stale health data actually control.
Certificates
Field reference and thresholds Expanding a certificate’s details shows: status, valid-from date, issuer, every domain it covers, serial number, SHA-256 fingerprint, and when it was last detected — deliberately excluding private key material.
Status thresholds Set from Administration → Security & Health:
Renewing-soon warning: days remaining before a certificate is flagged as renewing soon (8–120, default 30).Critical warning: days remaining before it’s flagged critical; must stay lower than the renewing-soon threshold (default 7).Stale health data: minutes before a displayed check is considered old and worth re-running (2–1440, default 10).A status of mismatch means a custom certificate was uploaded for that route but its coverage doesn’t include every configured domain — upload a replacement covering all of them.
Access Lists
Overview: protect a route An Access List restricts who can reach a Hosted Site, Proxy Host, or Redirect Host, by network address, by login, or both. Assign a saved list from that route’s Advanced options.
Fields Name. Allowed networks: one IP, CIDR range, or the literal private_ranges per line. When set, every network not listed is denied — this is an allow-list, not a suggestion.Denied networks Optional : evaluated before allowed networks and logins, so a denied entry always wins even if it would otherwise be allowed.Logins: add one or more username/password pairs directly on the Access List (passwords need at least 8 characters; leaving a password blank while editing an existing login keeps it unchanged).Groups: alternatively, let members of a Group (Administration → Users & Groups) authenticate with their own Site Gateway username and password instead of a separate Access-List-only login. A disabled Group (Administration → Users & Groups) stops granting access immediately, even though it still appears assigned here — see Users & Groups for details.At least one rule — a network rule, a denied-network rule, or a login — is required before the list can be saved.
Assigning and inspecting Use a route’s Advanced options to assign an Access List to it, or use the Access List’s own View assigned hosts menu action to see everywhere it’s currently in use. An Access List can’t be deleted while anything still references it — unassign it from every host first.
Users & Groups
Overview: control who can change the gateway Every account has a role:
Administrator: full access, including Users, Groups, Settings, and Backups.Standard User: can manage Hosted Sites, Proxy Hosts, Redirect Hosts, Streaming Hosts, and Access Lists, but not Users, Groups, Settings, or Backups.Viewer: read-only — can inspect everything but change nothing.Creating a user Display name. Username: 3–64 characters, letters/numbers/._-, must be unique.Role. Temporary password: at least 8 characters — share it securely and have the person change it after their first sign-in.Managing an existing user An administrator can change a user’s role from the card’s role dropdown, reset their password, archive them (a reversible soft-disable distinct from deleting), or delete them outright. Delete and Change icon live under the card’s “•••” menu, matching Hosted Sites and Proxy Hosts; role, password reset, and archive stay as visible buttons. You cannot disable, archive, delete, or change the role of your own account — another administrator has to do that — and Site Gateway always keeps at least one active Administrator, refusing any action that would leave zero. Resetting a user’s password here does not disable their two-factor authentication if they have it enabled — see My Account for how 2FA works. If a user is locked out of their own 2FA (lost authenticator, no recovery codes left), an administrator can disable it for them from the card’s “•••” menu — Disable 2FA appears there only when that user currently has 2FA enabled. This clears their authenticator, secret, and any unused recovery codes; they can set 2FA up again afterward if they choose to. The action is logged to the Audit log, since it removes a security control from someone else’s account.
Groups A Group is simply a named set of users. Its only purpose is authentication: assign a Group to an Access List so its members can log in with their own Site Gateway credentials instead of a separate Access-List-only login.
Every Group has its own enable/disable toggle, separate from deleting it. Disabling a Group immediately stops it from granting access through any Access List it’s assigned to — the assignment itself is untouched and still shows as assigned, but its members can no longer authenticate through it until the Group is re-enabled. This is a common surprise: disabling a Group is not the same as archiving it for later cleanup, it has an immediate access-control effect. If the Group is currently assigned to at least one enabled Access List, disabling it now asks for confirmation first, naming exactly which Access List(s) will stop authenticating that Group’s members — the same pattern already used when disabling an Access List that protects active hosts.
Audit history Administration includes an Audit log — a searchable, filterable, immutable record of who did what, filterable by outcome (success/failed) and free-text search across the user, action, and target. Audit entries can’t be edited or deleted.
My Account
Managing your own profile, password, and two-factor authentication My Account is available to every role — Administrator, Standard User, and Viewer alike — and only ever affects your own account. It sits in the sidebar next to Administration and Documentation.
Profile Shows your display name, username, and role. These are read-only here; an administrator changes them from Administration → Users.
Changing your password Enter your current password once, then your new password twice. This works for every role — previously, only an administrator could change a user’s password (Administration → Users → Reset password), which meant every routine password change had to go through an admin. My Account closes that gap for everyone’s own account.
Two-factor authentication (2FA) Optional and off by default for every account. When enabled, signing in requires your password plus a 6-digit code from an authenticator app (or a recovery code), using the standard TOTP algorithm (RFC 6238) — any authenticator app works, not a Site Gateway-specific one.
Enabling 2FA Choose Enable two-factor authentication. Scan the QR code with your authenticator app, or enter the shown key manually. Enter the 6-digit code it generates to confirm setup. Save the 10 recovery codes shown immediately afterward — each works once, and this is the only time they’re shown in full. Signing in with 2FA enabled After your username and password are accepted, you’re prompted for a code. Enter the current 6-digit code from your authenticator app, or one of your unused recovery codes if you don’t have the app available. A wrong code shows an inline error without sending you back to the username/password screen.
Disabling 2FA, or regenerating recovery codes Both require re-entering your current password as confirmation. Regenerating recovery codes immediately invalidates the previous set.
An administrator resetting your password does not disable your 2FA — you’ll still need your authenticator app or a recovery code at your next sign-in. Only you can turn off your own 2FA, from My Account.
Administration
System A read-only operations and diagnostics page — what’s configured, what’s running, and what this deployment can do. The only things you can actually change here are the Docker container-picker toggle and the action buttons; everything else is status.
Environment & integrations Shows whether the Docker socket is mounted (and, if so, the toggle that lets Proxy and Streaming Hosts pick a running container as their target — moved here from Gateway Defaults) and whether BACKUP_PASSWORD is configured on the container (the actual "Encrypt scheduled backups" toggle it enables stays on the Backup & Restore page, next to the setting it drives).
Security status Read-only flags for whether ADMIN_PASSWORD or SESSION_SECRET are still on their built-in defaults, and whether ACME_EMAIL is set.
Gateway sync Current configuration-drift status and a persistent Resync now control — the same action available inline on the Dashboard’s Needs Attention list, always available here too.
Scheduled jobs and storage A table of background jobs with their schedule and last result, and a per-folder breakdown of disk usage under /data (sites, backups, certificates, logs, database) with total used and remaining capacity.
Reload & restart Reload gateway config re-applies the current configuration to Caddy with no downtime and is always available. Restart application stops and restarts the whole application — it only becomes available when the Docker socket is mounted and the container’s own restart policy (checked via the Docker Engine API) is always, unless-stopped, or on-failure; without a qualifying restart policy, the container would not come back on its own, so the button stays disabled with an explanation instead.
Administration
Gateway Defaults: handling unknown addresses Controls what happens when a visitor reaches Site Gateway on HTTP using a hostname that isn’t configured. (Unknown HTTPS hostnames are always rejected outright, regardless of this setting, since serving anything else would need a certificate Site Gateway doesn’t have — and issuing a misleading one would be worse.)
Response modes Themed route-not-found page (404) — the safest public default.Gateway ready page (200) — useful while confirming HTTP routing during initial setup.No response — close connection. Redirect elsewhere: set a destination URL, redirect code, and whether to preserve the requested path and query.Custom HTML: administrator-authored markup, served exactly as written with no sanitization — up to 250,000 characters.The themed page’s heading and explanation text are also editable here, independent of which mode is active. It displays the Site Gateway icon and wordmark, matching the branding used throughout the rest of the app.
Live preview Configuration drift detection Every 10 minutes (and once shortly after startup), Site Gateway compares Caddy’s live running configuration against what your saved routes would currently generate. If they disagree — for example after a manual edit outside the app, or a Caddy restart that didn’t pick up the latest reload — a “Configuration drift” item appears on the Dashboard’s Needs Attention list with its own inline “Resync now” button, which simply re-runs the normal save-and-reload path and clears the flag; it does not change any of your saved settings. The first time drift is detected, it’s also recorded as a Gateway Events entry.
Administration
Backup & Restore A Configuration only backup contains a consistent SQLite snapshot of every route, user, group, Access List, and setting, plus a portable JSON export of the same data. A Complete backup adds uploaded Hosted Site files, icons, the default-site page, and certificates (both managed and custom).
Scheduled backups Enable scheduled backups. Type: Complete (the recommended default) or Configuration only. If scheduled backups are enabled while Configuration only is selected, a warning appears explaining that Hosted Site files, icons, and certificates won’t be included.Schedule: Daily, Weekly, or Monthly, plus the hour of day to run.Keep: how many scheduled backups to retain (1–100, default 7) — older ones beyond this count are deleted automatically after each run.Include logs. Encrypt scheduled backups: uses the container’s BACKUP_PASSWORD environment value. This checkbox is disabled with an explanation if BACKUP_PASSWORD isn’t currently set on the container — it can’t be turned on until that value exists. If it was previously enabled and BACKUP_PASSWORD was later removed from the container’s environment, it stays checked but shows a warning instead of failing silently at the next scheduled run — set the variable again (and restart the container) to resolve it.Manual backup, encryption password The optional backup password field on this page is used only for manually created backups and for restoring an encrypted archive — it is never stored by Site Gateway. A manually created backup is saved to /data/backups and appears in the list below; it does not download automatically — use that entry’s Download button when you want a local copy.
Restore checklist Download or import the .sgbackup archive (importing just stages the file — restoring is a separate, explicit action). Supply its password if it’s encrypted. Choose Restore and allow validation to finish. Confirm hosts, certificates, and upstream health afterward. Site Gateway verifies every file’s checksum and automatically creates a safety backup of the current state before restoring anything. If the restored configuration turns out to be invalid, it automatically rolls back to that safety backup rather than leaving the gateway in a broken state.
Configuration safety & updates Site Gateway validates every generated Caddy configuration before reload and keeps the previous working configuration active if validation fails. Container updates are installed by pulling a new pinned image — create a backup first.
Administration
Logs & Retention Set how many days of Access, Activity, Audit, Certificate, and Security records to keep (7–3650 days each) and whether automatic pruning is enabled. Prune Now shows exactly how many records in each category are eligible before you confirm, and Download Logs exports them.
Administration
Danger Zone Two separate, deliberately distinct destructive actions — kept apart so a routine preference correction is never confused with a full rebuild. Both require re-entering your own administrator username and password, typing an exact confirmation phrase, and then confirming a second themed dialog by typing YES .
Restore Defaults Confirmation phrase: RESTORE DEFAULT. Resets Gateway Defaults, backup schedule settings, and certificate-health thresholds back to their starting values. It does not remove any hosts, uploaded files, users, groups, Access Lists, certificates, logs, or backups.
Factory Reset Confirmation phrase: FACTORY RESET. Deletes everything under /data — every host of every kind, uploaded files, certificates (managed and custom), logs, backups, icons, users, and settings. Docker-mounted files outside /data are untouched. The container returns to the initial setup screen without needing a manual restart. Because backups themselves live under /data/backups, they’re deleted too — recovery is only possible from a backup taken beforehand and stored elsewhere (downloaded, or on separately mounted storage).
When to use a backup instead If you want to undo a recent change while keeping the rest of the installation intact, restore a backup — Factory Reset is not a rollback tool.
Logs
Access Logs and Gateway Events Access Logs Every request Caddy handles, with domain, path, response status, latency, and upstream outcome. Filter by host or by status-code range (2xx/3xx/4xx/5xx). Sensitive query-string values — tokens, secrets, passwords, session identifiers, API keys, credentials — are redacted before they’re ever stored, regardless of filter settings.
Gateway Events Configuration and operational changes, filterable by severity (Normal/Warnings/Errors) and by category (certificate, health, authentication, backup, configuration, or system).
Example Filter Access Logs for a 502, then compare the target address against a direct LAN request to the same upstream to isolate whether the problem is the gateway or the application itself.
Performance
Throughput by domain, over time Built entirely from the same request data already collected for Access Logs — no new logging or extra overhead, just a different view of it.
Filters Domain narrows the Trend chart to one host (default: all domains combined). Range sets the Trend window — Last hour, 3, 6 (default), 12, or 24 hours, or 3 or 7 days.
Trend Request volume over the selected range, bucketed into 15-minute (up to 24h) or hourly (3–7 days) points.
Per-route table One row per domain that has received traffic, sorted by 24-hour volume:
Last hour / Last 24h: request count for that window; a red count after the divider is how many of those responses were 4xx or 5xx. This includes routine noise (expired-token 401s, bots probing by raw IP, scanners hitting invalid hosts) as well as genuine failures — it isn’t a health verdict by itself.Avg. response: the mean response time across every request to that domain in the last 24 hours — a fixed 24h window regardless of the Range filter above, and a straight average, so a handful of slow outliers can pull it up more than most visitors actually experience.Top paths: a per-domain “View” popout listing the top 10 most-requested paths on that domain in the last 24 hours, with request counts.Common Interface Controls
Menus, toggles, and role gating The same card language is used throughout Hosted Sites, Proxy Hosts, Redirect Hosts, Streaming Hosts, Access Lists, Groups, and Users, so learning one area transfers directly to the next.
The three-dot menu Contains actions that change or inspect a card: Edit opens the full form, kind-specific extras appear where relevant (Replace files on Hosted Sites, View assigned hosts on Access Lists), and Delete removes the record after a confirmation.
The toggle switch A slide switch, separate from the menu, turns a route or account on or off without deleting its saved configuration — useful during maintenance or testing when you expect to reuse the exact same settings shortly after.
Who can use each control Administrators can manage everything. Standard Users can manage Hosted Sites, Proxy Hosts, Redirect Hosts, Streaming Hosts, and Access Lists, but not Users, Groups, Settings, or Backups. Viewers can inspect information but cannot create, edit, disable, assign, or delete anything — their menus and toggles are hidden entirely. This is enforced on the server independently of what the interface shows, so hiding a button is a convenience, not the actual security boundary.
Update notifications While signed in, Site Gateway checks every 60 seconds whether a newer version has been deployed. If so, a small banner appears with Refresh and Dismiss — Refresh reloads the page to pick up the new version; Dismiss hides the banner, but it reappears on the next check if you’re still on the old version. This only matters for a tab left open across a deploy; closing and reopening the tab, or signing in fresh, always loads the current version automatically.
Icons
Changing a card’s icon Every Hosted Site, Proxy Host, Redirect Host, Streaming Host, Access List, Group, and User can have its own icon. Open the picker from a card’s icon tile or its “Change icon” menu action.
Search by service name: type at least 2 characters (e.g. Jellyfin, Plex) to search a large built-in icon catalog and pick a match.Upload a custom image: PNG, JPEG, WebP, GIF, or SVG, up to 2 MB, stored locally under /data/icons. Uploaded SVGs are checked for embedded scripts or external references before being accepted.Image URL: paste a direct https:// image link instead of uploading a file.Use two-letter fallback: clears any icon and reverts to initials derived from the name.If a custom icon URL ever stops loading, the card automatically falls back to showing initials instead of a broken image.
Troubleshooting
When HTTPS is not detected Confirm public DNS actually points to this server, that router/firewall forwarding reaches ports 80 and 443, and that NGINX Proxy Manager or another reverse proxy isn’t still holding those ports. Then check Certificates and Logs → Gateway Events for the specific rejection reason. Site Gateway cannot request a public certificate while another gateway is receiving the ACME challenge on its behalf.
A route rejected its configuration Check the error shown on save — it names the specific problem (duplicate address, invalid upstream, malformed custom configuration, certificate/TLS issue) rather than a generic failure, and the previous working configuration stays active while you fix it.
A Streaming Host isn’t reachable from outside This is almost always the port not being published on the container yet — see Streaming Hosts → Overview for the docker-compose/Unraid port mapping requirement.
A user is locked out after enabling two-factor authentication If they still have an unused recovery code, they can sign in with it in place of the 6-digit code. If not, an administrator can disable that user’s 2FA from Administration → Users — open the locked-out user’s “•••” menu and choose Disable 2FA . The user can sign in with just their password afterward and set 2FA up again whenever they’re ready.
No guide matched that search.
@@ -437,6 +439,6 @@
A new version of Site Gateway is available. Refresh Dismiss
-
+