clientproxy.io
→ console
← All documentation

Publish an ESP32 control UI over HTTPS

Make an ESP32 dashboard accessible through ClientProxy while keeping its frontend files off the device's tunnel connection. The proxy serves the React UI from App File Cache; a tunnel client on your LAN forwards small API requests to the ESP32.

Browser → HTTPS → ClientProxy → cached HTML, JavaScript, CSS
                           └→ tunnel client on your LAN → ESP32 HTTP API

This guide uses the esp32-hosted-ui example. Download or clone its source before following the commands. Its tunnel client runs on a NAS, Raspberry Pi, home server, or computer that can reach the device. Keep that machine powered on while using the public dashboard.

What you need

  • A board supported by the example: ESP32-S3, ESP32 DevKit, or ESP32-C3.
  • PlatformIO, a USB connection to the board, and Wi-Fi.
  • The esp32-hosted-ui example source, including its firmware, ui/, and packaging script.
  • Node.js 20 or newer, npm, zip, and unzip.
  • A ClientProxy tunnel with an eligible assigned product and App File Cache access.
  • The tunnel-client on a machine on the ESP32's LAN.

1. Flash and provision the device

In the example project's root directory, flash the environment for your board:

pio run -e esp32-s3 --target upload
pio device monitor -b 115200

For an ESP32 DevKit use -e esp32dev; for an ESP32-C3 use -e esp32c3.

On first boot:

  1. Join the device's esp32-hosted-ui-XXXXXX Wi-Fi network.
  2. Open the captive portal, or browse to http://192.168.4.1.
  3. Enter your Wi-Fi credentials and a unique device admin password of 8–64 characters.
  4. After restart, read the device's LAN address from the serial monitor, for example 192.168.1.42.

The device admin password protects its APIs. It is separate from your ClientProxy API Key. Do not put either secret in the frontend source or cache ZIP.

2. Check LAN access

From the machine that will run the tunnel client, open:

curl -i http://192.168.1.42:80/

Replace the example IP with your device's address. You should receive the example's explanation page. Reserve the ESP32's address in your router's DHCP settings so it stays stable.

3. Configure the tunnel

In the ClientProxy dashboard, create a tunnel, select a proxy server, and assign an eligible product. Add a domain mapping:

  • Select Auto-generate default domain for your first test.
  • Set Local IP:port to the ESP32's LAN address, for example 192.168.1.42:80.
  • Copy the generated domain, Tunnel ID, and API Key.

Start the client on the LAN machine:

tunnel-client \
  --api-url https://api-eu.clientproxy.io/api \
  --tunnel-id YOUR_TUNNEL_ID \
  --api-key YOUR_API_KEY

Choose api-us, api-eu, or api-asia for your setup. Keep TLS enabled for this desktop/NAS client.

Use the ESP32's LAN IP in the mapping. 127.0.0.1 would point to the machine running the tunnel client. At this stage, the public HTTPS domain should show the same explanation page as the local HTTP address.

4. Build and package the React UI

From the example project's root:

cd ui
npm ci
npm run build
cd ..
bash scripts/package-ui.sh

The script creates dist/esp32-hosted-ui.zip. It puts index.html at the archive root alongside assets/, omits source maps, and checks the 10 MB upload limit.

If packaging manually, ZIP the contents of ui/dist/, not the enclosing directory. The proxy must find index.html directly at the archive root.

5. Upload and link App File Cache

  1. Open App File Cache in the dashboard and create an entry, such as ESP32 UI v1.
  2. Choose Upload Zip and upload dist/esp32-hosted-ui.zip.
  3. Open your tunnel's Domains and edit the mapping to the ESP32.
  4. In Link cache file, select the new cache entry and save.
  5. Reconnect the tunnel client so the proxy fetches the archive.

Open https://YOUR_GENERATED_DOMAIN. You should see the React dashboard. Enter the device admin password when the UI requests it.

HTML, CSS, and JavaScript come from the proxy. The example's extensionless /api/* routes go through the tunnel to the ESP32.

6. Verify that the UI controls the real device

  1. Open Overview and check the live uptime and LAN IP.
  2. Change the device name in Settings, save, and refresh to confirm it persisted.
  3. In browser developer tools, inspect /api/status and confirm it returns live device data.
  4. Temporarily disconnect the ESP32. The cached frontend should still load, while API requests fail. Reconnect it and verify recovery.

The example also provides firmware updates. Use firmware built for the exact board and keep the page open until the update finishes. The UI sends firmware in sequential 12 KB multipart requests.

Updating the frontend

Build and package the new UI, create a new cache entry, upload the ZIP, link the new entry to the domain, and reconnect the tunnel client.

The proxy may retain an existing cache ID's files across reconnects. A new cache entry avoids continuing to serve the old frontend after overwriting an archive.

Troubleshooting

ProblemWhat to check
Device does not join Wi-FiCheck provisioning; the supplied GPIO0 configuration supports holding BOOT for three seconds at boot to reset it
UI loads, but APIs failCheck device power, LAN IP, tunnel client, and the domain's backend mapping
API returns an authentication errorUse the device admin password, not the tunnel API Key
Browser shows the explanation pageConfirm the cache is linked, the ZIP has a root index.html, and the client reconnected
Browser shows the previous UICreate and link a new cache entry, reconnect, then refresh
UI works locally but shows sample valuesThe local Vite preview uses sample data; verify live /api/status on the public domain

For a different design where the tunnel firmware itself runs on the ESP32, use the separate proxy-esp32 project. That requires its own walkthrough and has different memory, concurrency, and throughput limits.