Host a website on an ESP32#
A personal website, and the server is a €5 microcontroller. No VPS, no Raspberry Pi gateway, no router port forwarding: the ESP32 holds the whole page in flash and answers every visit itself, at a real HTTPS address. It's a slightly ridiculous experiment, and a good first build, because it touches every step you'll use for anything bigger.
What you need#
- An ESP32 dev board (ESP32 DevKit, or an S2, S3 or C3 board) and a USB cable.
- VS Code with the PlatformIO extension.
- Your page as one HTML file, with its CSS inside it.
- A free Tollan account in the console.
How it works#
browser ──HTTPS──▶ portfolio-byrne.a.tollan.app
▲
│ the board connects out
ESP32 ────────────────────────┘
serves index.html from flashThe page is compiled into the firmware. When a visit arrives, the sketch sends the file back, and Tollan puts it on the internet behind a managed HTTPS certificate.
Step 1: Name it and pick your board#
In the console, add a device named portfolio. Choose the esp32 (Arduino / PlatformIO) architecture and your board, such as ESP32 DevKit (classic ESP32). Leave provisioning on Enrollment: the board makes its own key on first boot.
Step 2: Expose port 80#
Add a route with Internal host 127.0.0.1, Internal port 80 and a Hostname (SNI) exposure. The form previews your free HTTPS address, something like portfolio-byrne.a.tollan.app.
Step 3: Download the demo project#
Click Download demo project. It's a complete PlatformIO project for this board: the library, the board's src/tollan_config.h and a starter sketch.
Important
The link works once and expires after 15 minutes, and the token inside enrolls one board. Keep src/tollan_config.h out of git until the board has enrolled.
Step 4: Add your page and flash it#
Save your page as src/index.html, and tell PlatformIO to build it into the firmware by adding one line to platformio.ini:
board_build.embed_txtfiles = src/index.htmlThen replace src/main.cpp with this, and fill in your Wi-Fi details (2.4 GHz only):
// A whole website, served by an ESP32 at its own HTTPS address.
#include <WiFi.h>
#include <TollanAgent.h>
#include "tollan_config.h" // from the console's demo project
using namespace tollan;
static const char* WIFI_SSID = "your-wifi";
static const char* WIFI_PASS = "your-wifi-password";
TollanAgent agent;
// src/index.html, linked in by board_build.embed_txtfiles (NUL-terminated).
extern const char INDEX_HTML[] asm("_binary_src_index_html_start");
// Send one complete HTTP response on a channel, then close it.
static void reply(uint32_t ch, const char* status, const char* type, const char* body, size_t len) {
char head[160];
int n = snprintf(head, sizeof(head),
"HTTP/1.1 %s\r\nContent-Type: %s\r\nContent-Length: %u\r\n"
"Connection: close\r\n\r\n",
status, type, (unsigned)len);
agent.send(ch, (const uint8_t*)head, n);
for (size_t off = 0; off < len; off += 1024) { // send in 1 KB pieces
agent.send(ch, (const uint8_t*)body + off, len - off < 1024 ? len - off : 1024);
}
agent.closeChannel(ch);
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(300);
agent.log().printf("wifi ok, ip=%s\n", WiFi.localIP().toString().c_str());
TollanConfig cfg;
cfg.host = TOLLAN_RELAY_HOST;
cfg.port = TOLLAN_RELAY_PORT;
cfg.caCert = CA_CERT;
#ifdef TOLLAN_ENROLL_TOKEN
cfg.enrollUrl = TOLLAN_ENROLL_URL; // first boot: the board makes its key and enrolls
cfg.enrollToken = TOLLAN_ENROLL_TOKEN;
cfg.enrollCaCert = TOLLAN_ENROLL_CA;
#else
cfg.clientCert = DEVICE_CERT;
cfg.clientKey = DEVICE_KEY;
#endif
cfg.renewUrl = TOLLAN_RENEW_URL;
cfg.deviceId = TOLLAN_DEVICE_ID; // health check-in for the console
cfg.targetsUrl = TOLLAN_TARGETS_URL;
// The page at /, a 404 for anything else.
agent.onData([](uint32_t ch, const uint8_t* data, size_t len) {
if (len >= 6 && memcmp(data, "GET / ", 6) == 0) {
reply(ch, "200 OK", "text/html; charset=utf-8", INDEX_HTML, strlen(INDEX_HTML));
} else {
reply(ch, "404 Not Found", "text/plain", "not found\n", 10);
}
});
agent.begin(cfg);
}
void loop() {
agent.loop();
delay(2);
}embed_txtfiles turns the file into a string in flash, so the sketch never touches a filesystem. Click Upload.
Step 5: It's online#
The device turns online in the console within seconds of the board joining Wi-Fi. Open your address: on the free plan a browser sees a short Tollan notice the first time; click Continue and there's your site, served from a board on your desk.
Gotchas#
- Only
/exists. The sketch serves one page and answers everything else with a 404. For a second page, add anothermemcmpbranch and a second embedded file. - Keep it to one file. Put the CSS in a
<style>tag and use SVG or small inline images. A large page works, but every visit streams the whole file over the board's Wi-Fi. - Changing the page means re-flashing. Edit
src/index.htmland click Upload again; the board keeps its identity, so there's nothing new to download.
Where to take it next#
- Make the page live: ESP32 sensor dashboard adds readings from a DHT22.
- Make it do something: Remote heating for a holiday home adds a relay and a button.