ESP32/Web server/54. Async web server
Your chip
Your language
Web server · 54 of 81

Async web server

A page served by the board itself, so a phone on the same Wi-Fi can see your readings with no app and no cloud. It is the most satisfying thing this chip does, and it goes wrong the moment a second person opens it.

/esp32/async-web-server · arduino · S3

One tab hides the problem

WebServer (synchronous)
9 requests
Phones and tabs open3
Work inside one handler120 ms
Requests in flight
9
Worst wait
1.1 s
Feels
sluggish
Fine here, and fine is a trap. One tab hides the queue completely. Open the page on a second phone, or leave a tab polling every second, and the wait multiplies — which is why this shows up in someone's living room and never on your bench.

The shape that works

  • HTML in flash, data over JSON. Put index.html on LittleFS and serve it statically. The page then fetches /api/now every second and updates itself. Building HTML strings in C++ is the thing people do first and regret.
  • Handlers never block. They read a variable, they answer, they return. loop() keeps that variable fresh.
  • Under 50 ms per handler. Past that, a phone with four connections open starts to feel broken.

When to reach for WebSockets instead

Polling every second is fine for a temperature. For anything that changes fast, or where the board needs to push — a button state, a live graph, a log — a WebSocket is one persistent connection instead of a new request per second, and AsyncWebSocket is part of the same library.

Serving to the internet

Don't forward a port. The board has no certificate, no authentication and a TCP stack that is small on purpose. Push to an MQTT broker, or use a tunnel, and keep the web server for the local network where it belongs.

On your S3
ChipXtensa LX7 · 2 × 240 MHz
Board settingESP32S3 Dev Module
Default I2CSDA 8 · SCL 9
Watch out forThe port vanishes after upload

The code

Two routes - one HTML page from flash, one JSON endpoint the page polls. Keeping the data separate from the page is what lets the browser update without reloading.

async_server.ino
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <LittleFS.h>

AsyncWebServer server(80);
float temperature = 0;          // kept fresh by loop(), read by handlers

void setup() {
  Serial.begin(115200);
  LittleFS.begin(true);
  WiFi.begin("your-network", "your-password");
  while (WiFi.status() != WL_CONNECTED) delay(250);
  Serial.println(WiFi.localIP());

  server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html");

  server.on("/api/now", HTTP_GET, [](AsyncWebServerRequest *r) {
    r->send(200, "application/json",
            "{\"temp\":" + String(temperature, 1) + "}");
  });

  server.onNotFound([](AsyncWebServerRequest *r) {
    r->send(404, "text/plain", "no such page");
  });

  server.begin();
}

void loop() {
  static unsigned long last = 0;
  if (millis() - last >= 1000) {
    last += 1000;
    temperature = 20 + (millis() % 5000) / 1000.0;   // your sensor here
  }
}

Handlers are callbacks and they must not block. No delay, no waiting on a sensor, no long loops. Read a variable that loop() keeps fresh, answer, and return.

When it does not work

The page loads for you and hangs for a second phone

You are using the synchronous WebServer, which handles one request at a time. A browser opens several connections for one page, so it queues against itself too.

The board resets when the page loads

A handler is blocking. delay inside an async callback stalls the whole TCP stack. Move slow work into loop and have the handler read the result.

The page appears and the readings never update

The browser cached the JSON. Add a cache-control header to the API route, or a changing query parameter on the fetch.

It works over Wi-Fi and not from outside the house

That is correct and it is not a bug. The board has a private address. Reaching it from the internet needs a tunnel or a broker - see MQTT rather than opening a port on your router.

Where this goes next

The board has spent this chapter answering. The next page turns it round and has it ask.

HTTP from a board

Edit this page — content/esp32/async-web-server.mdx

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse ESP32 on the forum