Ask
28
@proxy_pilgrim ·

Self-hosted app refuses to load behind my reverse proxy: "access through untrusted domain" — and adding the domain in the container does not stick

I put a self-hosted app behind my reverse proxy so I can reach it at a real hostname with a real certificate instead of an IP and a port number.

It now refuses to load and shows a message about being accessed through an untrusted domain, telling me to add the hostname to a trusted domains list in the configuration.

I found the configuration file inside the container, added the hostname, and it worked — until the container restarted, at which point my edit was gone and I was back to the same error page.

So two questions. Why is the app rejecting a hostname that resolves and serves correctly, and how do I make the setting survive a redeploy?

3 answers Share
Report

Answering anonymously — a moderator will review it first.

  • @proxy_pilgrim · 4h ago

    Both questions have the same root, so let me take the second one first because it is the one that will keep biting you.

    Anything you edit inside a running container is written to that container's writable layer, and the writable layer is destroyed when the container is replaced. Every update, every redeploy, every restart of a container that is recreated rather than resumed — gone. This is not a quirk of this app, it is how containers work, and it is why every serious image exposes its settings through environment variables or a mounted config file.

    So: set the trusted hostname either as an environment variable in your deployment, or in a config file that lives on a mounted host path rather than inside the image. Those two survive. Nothing else does.

    Now the first question, which is worth understanding rather than just working around.

    The app is not checking DNS. It is checking the Host header on the incoming request against an allowlist, and refusing anything not on it. That is a deliberate defence: an app that trusts whatever hostname a request claims can be tricked into generating password-reset links, redirects and asset URLs pointing at an attacker's domain. The check is a nuisance exactly once, when you legitimately add a new name.

    While you are in there, set the other two settings that matter behind a proxy, because you are about to hit them:

    The public URL, protocol and host. Your proxy terminates TLS and talks to the app over plain HTTP on the internal network. Left alone, the app sees an HTTP request and builds every absolute link — reset emails, share links, client config — as http:// on the internal address. Users get links that do not work from outside. Setting the overwrite protocol to https and the overwrite host to your public hostname fixes it.

    Trusted proxies. The app should log the real client address, not your proxy's. It will only believe the forwarded-for header if it knows the request came from a proxy it trusts, so give it your proxy's address. Get this wrong in the other direction — trusting everything — and any client can forge its own source address in your logs and in any rate limiting you do.

    Set those three together, redeploy once, and the whole class of "works internally, broken externally" problems goes away.

    30
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @compose_by_hand · 4h ago

    Concrete on the persistence point, because "mount a config file" trips people up in a specific way.

    If you mount a single file over a path that the image expects to generate, and your file is missing something the app writes at first run, you can end up with an app that starts but behaves oddly. Safer pattern with most images: mount a directory that the app treats as its config directory, let the app populate it on first run, and then edit the file that appears there on the host.

    That way the file is the app's own file, in its own format, and you are editing it from outside rather than fighting the image over ownership of it.

    And whatever you end up with, put it in version control. Config that only exists on the box is config you will reconstruct from memory at the worst possible time.

    27
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @dataset_first · yesterday

    Worth adding both names, not one.

    If you reach the app at a public hostname from outside and at an internal name or IP from inside your network, both need to be trusted, or the internal path breaks the moment you get the external one working. People fix the external case, feel pleased, and then find that the backup script hitting it internally has been failing for a week.

    A lot of apps take a list rather than a single value for exactly this reason.

    20
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report