Custom HTML and iframe Access
Custom HTML pages, animation frames, reports and dashboards open inside an iframe served from a
separate sandbox port (8083 by default). The separate port is an isolation boundary: this
content runs with sandbox="allow-scripts", on its own origin.
This page covers the case where one installation is reached both from the local network and from an external address (typically Secure Access). Both access paths can work at the same time, but the browser-side permissions have to be granted for each path separately.
What decides the frame origin
Section titled “What decides the frame origin”The frame origin is built by the browser, not the server:
${location.protocol}//${location.hostname}:${sandboxPort}sandboxPort comes from the GET /api/config response. The result: whichever address the user
came in on, the frame opens on port 8083 of that same address. The frame therefore always
lands on the same host as the application, so host-only cookies reach it.
Which setting feeds what
Section titled “Which setting feeds what”There are three separate checks, and they are not fed from the same setting. For an access path to work, all three have to recognise it.
| Setting | What it feeds | Format |
|---|---|---|
ins.app.url, ins.sandbox.url, ins.frontend.url | Enters HostAllowlist as an exact origin — CORS and the sandbox Referer/Origin check | One value each |
ins.allowed-hosts | Adds a host regex to HostAllowlist; no port constraint | A single regex |
ins.iframe.allowed-hosts | The main page’s CSP frame-src and frame-ancestors, and the animation external-iframe validation | Comma-separated list |
Because ins.iframe.allowed-hosts has two different consumers, both forms go in the list:
- The CSP directives want concrete origins (
https://host:8083). Those directives have to be a static list: the parent origin is not known when a sandbox response is generated, so the frame origin derived at request time must already be present in the list. - The animation external-iframe validation compares the address by its bare host name
(
host). That validation is fail-closed: if the list is empty, every address is rejected.
Care when setting ins.allowed-hosts
Section titled “Care when setting ins.allowed-hosts”This regex replaces the local defaults rather than adding to them. The defaults are:
localhost127.*- the Secure Access tunnel network
10.66.66.*
If you do not include these in your own regex, tunnel access — or access from the server itself — that previously worked will break.
- Point
ins.app.urlandins.sandbox.urlat the external address. - Add the local network IP or machine name to the
ins.allowed-hostsregex — along withlocalhost,127.0.0.1and the tunnel network (see the warning above). - Add the following to
ins.iframe.allowed-hostsfor every access path:- the
8082and8083origins - the bare host name
- the
localhostand127.0.0.1origins too. These are HostAllowlist defaults, but they do not enter the CSPframe-srcon their own; without them, Custom HTML will not open when browsing from the server itself viahttps://localhost:8082.
- the
- For local network access,
8082and8083must be open to inbound connections in the firewall. Tunnel traffic is not subject to the firewall. - If the certificate does not cover the local IP, the browser has to accept the
8083certificate separately: openhttps://<local-ip>:8083directly once. Until it is accepted, the frame stays blank with no error.
Settings go in <install>\config\application.yml, after which the inSCADA service is
restarted. No JVM -D parameter and no change to the service definition is needed.
Example
Section titled “Example”ins: app: url: https://<company>.inscada.online sandbox: url: https://<company>.inscada.online:8083 allowed-hosts: '^(localhost|127\.0\.0\.1|<escaped-local-ip>|10\.66\.66\.\d{1,3})$' iframe: allowed-hosts: >- https://<company>.inscada.online, https://<company>.inscada.online:8083, https://<local-ip>:8082, https://<local-ip>:8083, https://localhost:8082, https://localhost:8083, https://127.0.0.1:8082, https://127.0.0.1:8083, <company>.inscada.online, <local-ip>, localhost, 127.0.0.1Values in angle brackets are placeholders. Inside the regex, the dots of an IP address are
escaped (192\.168\.1\.50).
Verification
Section titled “Verification”Two lines appear in the startup log:
HostAllowlist: using explicit override regex from ins.allowed-hostsHostAllowlist initialized. literalOrigins=[...], localPorts=[...], defaultsInEffect=falsedefaultsInEffect=false tells you your regex took effect and the defaults no longer apply —
which is exactly why step 3 carries its warning.
Then check the Content-Security-Policy response header on each access address:
frame-src must contain that address’s :8083 origin.
Finally, open a Custom HTML page or an animation in the browser. That exercises the sandbox origin and the cookie flow end to end.
Troubleshooting
Section titled “Troubleshooting”If a frame does not open, look at the iframe’s src in DevTools first — seeing which address
it tried tells you which list is missing it.
| Symptom | What it means |
|---|---|
Missing or invalid origin for sandbox content in the log | The address is missing from HostAllowlist — ins.allowed-hosts or the advertised addresses |
A frame-src / frame-ancestors CSP violation in the console | The address is missing from ins.iframe.allowed-hosts |
| Frame blank, no error | The 8083 certificate has not been accepted — open that address directly once |
Sandbox port used for illegal API call in the log | Normal. The sandbox port rejects /api calls |
Adding a new access address
Section titled “Adding a new access address”- Add the host to the
ins.allowed-hostsregex. - Add
https://<host>:8082,https://<host>:8083and the bare<host>toins.iframe.allowed-hosts. - Restart the service.