ChatGPTClaudeGrok / X / CursorGemini

DSH public access: public-url, trusted-host and TLS

Configuration & UsagePublished 2026-10-21Author: DeepSeek Plugin Market
DeepSeek Harnesspublic deploymentreverse proxytrusted-hostTLS
Publishing DeepSeek Harness needs --public-url to announce the address, --trusted-host to admit authority, and TLS for cookies. Miss one and APIs return 403.

Publishing DeepSeek Harness takes three settings that work together: --public-url announces the external address, --trusted-host admits the browser authority, and TLS protects the cookie. They are not interchangeable. With only public-url, every API returns 403. With only trusted-host, printed URLs are wrong. With only TLS, a subpath deployment still breaks on cookies.

DeepSeek Harness: What --public-url announces

--public-url changes presentation, not the routes the listener serves. The official scope:(source)

Announced locationPurpose
Printed and auto-opened URLCopy into the browser
DSH_WEB_URLRead by scripts and integrations
Web GUI URL in agent runtime contextTell the agent where the UI lives

It accepts an http:// or https:// root, optionally with a mount prefix, and normalizes it to a trailing slash. The listener still serves origin-root routes and never learns about /ui. In other words: public-url says “where I am”, not “move the listener there”.

The most common mistake is treating --public-url as authorization. The official docs are clear: the announced URL and the trust fence are separate.

DeepSeek Harness: What --trusted-host admits

If the browser authority is not on the fence list, every /api call returns 403 even when the proxy is perfect. The fence accepts three sources:(source)

  1. Loopback — direct access through 127.0.0.1 or localhost.
  2. The listener’s binding address — whatever address the listener binds.
  3. --trusted-host entries — repeatable; an entry without a port matches any port, which suits tunnels that bind different ports.

Configuration:

sh
dsh web \
  --public-url https://app.example/ui/ \
  --trusted-host app.example
  1. Read the browser authority — app.example or app.example:8443. Expected: the value matches exactly.
  2. Omit the port when it changes — a hostname-only entry matches any port. Expected: tunnels keep working after a port change.
  3. Restart and re-check — open the tokenized URL and inspect an /api request. Expected: no 403; if it persists, check whether the proxy rewrites Host.

The fence only admits requests. Authentication still comes from the startup token and the signed session cookie. TLS termination does not change that.

DeepSeek Harness: Five jobs for the reverse proxy

A proxy is not just “forward the request”; the official contract has five hard requirements. Check each:(source)

  1. Preserve the browser-visible Host — the fence compares it with the authority. Expected: the backend sees the external hostname.
  2. Strip the mount prefix — /ui/api/... must arrive as /api/.... Expected: no prefix reaches the listener.
  3. Forward upgrades — every request and WebSocket upgrade must carry Upgrade and Connection. Expected: real-time output keeps refreshing.
  4. Rewrite cookie scope — the backend issues a host-only Path=/ cookie; the proxy changes Path to /ui/ and adds Secure only on HTTPS. Expected: the browser returns the cookie only on that subpath.
  5. Redirect the bare mount — /ui must arrive as /ui/; only /ui/ exchanges the startup token, while /ui/index.html works once a cookie exists. Expected: no 404 on the bare mount.

Items 1 and 4 fail most often. The first produces 403; the second produces a login loop.

DeepSeek Harness: Three details that bite after “the page opens”

These are not TLS handshake problems, and each has a distinct symptom. Check them separately:(source)

  1. Host must stay intact — rewriting it to the listener address removes the external domain from the fence. Expected: the backend sees the browser authority.
  2. /ui must become /ui/ — only the slash form exchanges the token; a cookie holder can also load /ui/index.html. Expected: the bare mount does not 404.
  3. Cookie Path and Secure are separate — change Path to the mount and add Secure only on the HTTPS hop. Expected: no cookie leak over HTTP.

One more boundary: neither the announced URL nor the fence protects the listening port. Restrict the port to a trusted proxy or internal network; do not expose it just because TLS is on.

TLS can terminate at the proxy, at the listener, or at both; the difference is who adds Secure and who rewrites Path. Compare:(source)

CombinationCertificate locationCookie handling
Proxy terminates TLSProxyProxy rewrites Path and adds Secure on HTTPS
dsh web native TLSListenerListener issues Secure cookies; proxy only rewrites Path
Both terminateProxy and listenerEach hop encrypts separately

Native TLS uses --tls-cert and --tls-key; the certificate needs a full chain and the key must be unencrypted. Files are read once at startup, so rotation requires a restart or reload.

The browser-facing link should always be HTTPS. An http:// root exposes the startup token in clear text, and that token is a process credential.

DeepSeek Harness: Pre-flight checklist

Separate “the page opens” from “the app works”, or you will misread 403 as a TLS failure. Run in order:(source)

  1. Local listener — curl -vk https://127.0.0.1:3080/. Expected: TLS handshake succeeds.
  2. Proxy forward — curl -v https://app.example/ui/. Expected: the page returns, no 502.
  3. Response headers — inspect Set-Cookie for Path and Secure. Expected: Path=/ui/, Secure on HTTPS.
  4. API call — pick one /api request in devtools. Expected: 200, not 403.
  5. WebSocket — watch real-time output. Expected: the connection stays open.

If 1 and 2 pass but 4 returns 403, check --trusted-host and Host rewriting. If 3 is wrong, fix the proxy’s Path/Secure rewrite.

DeepSeek Harness FAQ

Can --public-url omit the scheme? No. The official flag expects an http:// or https:// root; without a scheme it cannot normalize or generate external URLs.

Can I use an IP address? Yes, but --trusted-host must contain the exact IP authority and the certificate must cover that IP, or name validation fails.

Does the fence protect the listening port? No. The official docs say neither the announced URL nor the fence protects the port itself. Keep the port behind a trusted proxy or internal network.

The short version: public access is not “add a proxy”; it is announced address, trust list, TLS, and cookie path aligned. 403 points at the authority; a login loop points at cookie Path.

ChatGPTClaudeGrok / X / CursorGemini

FAQ

What is the difference between --public-url and --trusted-host?

DeepSeek Harness uses --public-url only to announce the external address in printed URLs, DSH_WEB_URL, and agent context. --trusted-host controls the /api trust fence. Announcing a URL does not admit that authority, so a domain missing from --trusted-host still gets 403.

What must a reverse proxy do when dsh web is mounted under /ui/?

DeepSeek Harness: preserve the browser-visible Host, strip the /ui prefix before forwarding, forward Upgrade and Connection headers, rewrite the cookie Path from / to /ui/ and add Secure on HTTPS, and redirect bare /ui to /ui/. Otherwise the startup token cannot become a session cookie.

Why can I open the page but every API call returns 403?

DeepSeek Harness: the trust fence is rejecting the request, not authentication. It accepts loopback, the listener binding address, and authorities listed in --trusted-host. Add the exact browser authority and restart; TLS alone does not add it.

Should TLS terminate at dsh web or at the reverse proxy?

DeepSeek Harness: both work. A proxy is better for a shared domain, WAF, and multiple backends. Native TLS with --tls-cert and --tls-key is better for a single entry point. Either way, the TLS listener adds Secure to the session cookie, so the browser-facing link should stay on HTTPS.

What is the risk of setting --public-url to an http:// root?

DeepSeek Harness: a browser-facing http:// root exposes the startup token in clear text. The token can be exchanged for a session cookie, so treat it as a process credential and use an https:// root instead.

Related Terms

--public-url
--public-url is an announcement flag for dsh web. It accepts an http:// or https:// root, optionally with a mount prefix, normalizes it to a trailing slash, and feeds printed URLs and runtime context without changing the origin-root routes the listener serves.— DeepSeek Harness docs - Publishing the Web UI behind a reverse proxy
trust fence
The trust fence is the dsh web check that compares the Host or authority of /api requests against loopback, the listener binding address, and entries in --trusted-host. A mismatch returns 403.— DeepSeek Harness docs - Publishing the Web UI behind a reverse proxy
mount prefix
A mount prefix is the subpath a reverse proxy uses to expose the Web UI, such as /ui/. The listener still serves origin-root routes, so the proxy must strip the prefix before forwarding.— DeepSeek Harness docs - Publishing the Web UI behind a reverse proxy
startup token
A startup token is a per-process credential printed once by dsh. Opening the tokenized root URL exchanges it for a signed session cookie; it is not persisted and should not be shared with unintended users.— DeepSeek Harness docs - Publishing the Web UI behind a reverse proxy

Sources