Skip to content

Reverse-proxy anything on your network

Since v0.14.0, a gateway host with the web server option can act as an HTTP reverse proxy for its own network, the way nginxproxymanager does on a home server: pick a hostname, point it at http://192.168.1.10:3000, and get https://grafana.lab.example.com with a real certificate, an optional DedNets sign-in, custom headers, a custom error page and request logs.

The service being proxied does not run DedNets at all. The gateway reaches it over the network it already sits in, so anything the gateway can open in a browser, you can publish.

  • A host with the gateway role and the web server option (Publish on your own gateway), online.
  • A verified wildcard domain fronted by your web servers (front global), for example *.lab.example.com (Bring your own domain). Each proxy host takes a label under it.

The reverse proxy is off by default on every host, because it lets the Console point that machine at any address on its own network. Turn it on per host:

  • Console: open the host on the Hosts page and click Turn reverse proxy on. The pane shows a Proxy badge and a Proxy hosts table.
  • CLI: dednetsctl gateway proxy on alice/gw1
  • MCP: host_reverse_proxy_enable

Turning it off stops every proxy host on that machine at once. They stay recorded and come back when you turn it on again.

On the host pane, Add proxy host opens the editor:

  • Details: a name (it becomes the app name, alice/gw1/grafana), the upstream (http or https, host, port, optional path prefix), one or more hostnames under your wildcard domains, and Require a DedNets sign-in to put it behind the DedNets login (you and the accounts you add as viewers can open it; everyone else is stopped at the gateway).
  • Headers: headers to set on every request to the upstream (a fixed X-Real-IP, an Authorization for a backend that needs one) and on every response. Host, X-DedNets-*, Strict-Transport-Security, Server, Via and the connection-level headers are reserved.
  • Advanced: verify the upstream’s TLS certificate (off by default: home devices are usually self-signed), send the upstream’s own host as the Host header (for a backend that only answers its own name), and a custom HTML page shown when the upstream is down (502) or does not answer in time (504).

From the CLI:

Terminal window
dednetsctl export add -daemon alice/gw1 -name grafana \
-upstream http://192.168.1.10:3000 \
-request-header X-Real-IP=203.0.113.9 -response-header X-Frame-Options=DENY \
-error-page ./down.html
dednetsctl domain bind '*.lab.example.com' -label grafana -target alice/gw1/grafana

With an AI agent: service_reverse_proxy_add, then domain_bind.

The table shows each proxy host’s hostnames, upstream, the certificate state this host holds for each name (SSL), access, and a status: online when the gateway can reach the upstream, upstream down when it cannot, not served when the host refuses it (see below), switch off while the reverse proxy is off. An SSL error carries Let’s Encrypt’s explanation on hover and is retried automatically, five minutes after the failure and then doubling (since v0.14.2); see certificates on a gateway.

The hostname is served by the gateway itself: TLS terminates on your machine, the request goes straight to the upstream on your network, and nothing leaves the host. A request that arrives through one of your other web servers, or through the DedNets edge for an edge-front name, reaches the upstream through this gateway over the mesh. It keeps working through a DedNets outage.

The upstream sees the visitor’s hostname as Host (unless you chose the upstream’s), the usual X-Forwarded-For / X-Forwarded-Proto headers, and, behind a DedNets sign-in, X-DedNets-User and X-DedNets-Email. Responses lose Server and Via and gain HSTS, like every hostname your web servers serve.

An operator can cap the addresses a host will proxy to in the daemon’s config, whatever the account decides in the Console:

[gateway]
enabled = true
web = true
proxy_upstreams = ["192.168.1.0/24", "10.0.0.0/8"] # empty = any address

(or DEDMESH_GATEWAY_PROXY_UPSTREAMS="192.168.1.0/24,10.0.0.0/8"). An IP outside the list is refused when the proxy host is created, and shows as not served with the reason; a DNS name is resolved on the host at connect time and must resolve inside the list.

A hostname that points at your gateway but is not bound to anything gets the default site, one setting for your whole account (Settings, Default site; dednetsctl gateway default-site; web_default_site_set):

  • Refuse (the default): the TLS handshake fails and plain http redirects to https, as before.
  • DedNets page, 404, Redirect to a URL of yours, or Custom HTML.

Anything but Refuse is served on https with a self-signed fallback certificate for the unknown name, so a browser shows a warning before the page. That is expected: no certificate authority issues for a name nobody bound. Plain http serves the default directly.

  • A proxy host is an app: uptime monitors, health, viewers and request logs work on it like on any other. dednetsctl apps shows where it points and, when the host refuses to serve it, why; an AI agent sees the same in service_list and service_status, which also says when a proxy host is waiting for the switch to be turned on.
  • Change the upstream or the options later from the editor, with dednetsctl export add again (it replaces the proxy host), or with PATCH /api/v1/exports sending only upstream or proxy.
  • Hosts running a version older than this feature ignore proxy hosts (the row stays pending until the host updates).
  • A true wildcard proxy (*.lab.example.com → one upstream) is not available; bind one label per proxy host.