ESP32 S3 - Web Server Multiple Pages

In this tutorial, we will explore how to turn an ESP32 S3 into a web server capable of handling multiple pages simultaneously, such as index.html, temperature.html, led.html, and error_404.html. The ESP32 S3's built-in Wi-Fi makes it an excellent candidate for hosting lightweight web interfaces directly from the microcontroller.

What you'll build:

  1. A multi-page web server running on the ESP32 S3 board.
  2. Individual HTML pages for home, LED control, temperature display, and 404 error handling.
  3. Dynamic page content updated with real-time sensor values from the ESP32 S3.
  4. HTTP request routing with proper error code responses such as 404 Not Found.
ESP32 S3 web server with multiple pages - home, LED, temperature and 404 pages

Hardware Preparation

1×ESP32 S3 WROOM N16R8
1×Alternatively, ESP32 S3 SuperMini Dev Module
1×Alternatively, ESP32 S3 Uno-form Board
1×Alternatively, ESP32 S3 Camera Board
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×Recommended: Screw Terminal Expansion Board for ESP32 S3
1×Recommended: Breakout Expansion Board for ESP32 S3
1×Recommended: Power Splitter for ESP32 S3

Or you can buy the following kits:

1×DIYables ESP32 S3 Starter Kit (ESP32 S3 included)
1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables .

Overview of ESP32 S3 and Web Server

The ESP32 S3 is a powerful dual-core microcontroller with integrated Wi-Fi and Bluetooth, making it well suited for web server applications. Its generous flash and PSRAM capacity (N16R8 variant) allows hosting multiple HTML pages and handling concurrent HTTP requests with ease. If you are new to the ESP32 S3 or web server concepts, the following tutorial provides the foundation you need.

Key Specifications

ESP32 S3 Code - Multiple-page Web server

The following code creates a web server on the ESP32 S3 that routes requests to multiple pages. To keep things straightforward, the HTML content for each page is simple and embedded directly in the code — in the next section, we will learn how to separate each page's HTML into its own file for better organization and maintainability.

/* * This ESP32 S3 code was developed by newbiely.com * * This ESP32 S3 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3/esp32-s3-web-server-multiple-pages */ /* * This ESP32-S3 code is created by esp32io.com * * This ESP32-S3 code is released in the public domain * * For more detail (instruction and wiring diagram), visit https://esp32io.com/tutorials/esp32-s3-web-server-multiple-pages */ #include <DIYables_ESP32_WebServer.h> #define LED_PIN 38 // ESP32-S3 pin GPIO38 connected to LED int LED_state = LOW; float getTemperature() { // YOUR SENSOR IMPLEMENTATION HERE // simulate the temperature value float temp_x100 = random(0, 10000); // a ramdom value from 0 to 10000 return temp_x100 / 100; // return the simulated temperature value from 0 to 100 in float } // WiFi credentials const char WIFI_SSID[] = "YOUR_WIFI_SSID"; // CHANGE IT const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD"; // CHANGE IT // Create web server instance DIYables_ESP32_WebServer server; // Page handlers void handleHome(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { Serial.println("Web Server: home page"); server.sendResponse(client, "This is the ESP32 S3 home page"); } void handleTemperature(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { Serial.println("Web Server: temperature page"); float temperature = getTemperature(); String reponse = "Temperature: " + String(temperature); server.sendResponse(client, reponse.c_str()); } void handleLed(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { Serial.println("Web Server: LED page"); // Check for the 'state' parameter in the query string String state = ""; for (int i = 0; i < params.count; i++) { if (String(params.params[i].key) == "state") { state = params.params[i].value; if (state == "on") { LED_state = HIGH; } else if (state == "off") { LED_state = LOW; } // control LED here digitalWrite(LED_PIN, LED_state); Serial.print(" => turning LED to "); Serial.print(state); Serial.println(); break; } } String reponse = "LED state: " + String(LED_state); server.sendResponse(client, reponse.c_str()); } void handleNotFound(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { String response = "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>404 Not Found</title></head><body>"; response += "<h1>404 - Page Not Found</h1>"; response += "<p>Sorry, we couldn't find that page!</p>"; response += "<a href=\"/\">Return to Home</a></body></html>"; server.sendResponse(client, response.c_str()); } void setup() { Serial.begin(9600); delay(1000); pinMode(LED_PIN, OUTPUT); Serial.println("ESP32 S3 Web Server"); // Configure routes server.addRoute("/", handleHome); server.addRoute("/temperature.html", handleTemperature); server.addRoute("/led.html", handleLed); // Set custom 404 handler server.setNotFoundHandler(handleNotFound); // Start web server with WiFi connection server.begin(WIFI_SSID, WIFI_PASSWORD); } void loop() { server.handleClient(); }

Detailed Instructions

  1. New to ESP32 S3? Complete our Getting Started with ESP32 S3 guide first.
  2. Do the wiring as shown in the image above.
  3. Connect the ESP32 S3 board to your PC via a USB Type-C cable.
  4. Open Arduino IDE on your PC.
  5. Select the right ESP32 S3 board (e.g. ESP32S3 Dev Module) and the correct COM port.
  6. Open the Library Manager by clicking on the Library Manager icon on the left navigation bar of Arduino IDE.
  7. Search "DIYables ESP32 WebServer", then find the Web Server library created by DIYables.
  8. Click Install button to install the Web Server library.
ESP32 S3 Web Server library
  1. Copy the above code and open it with Arduino IDE.
  2. Change the WiFi information (SSID and password) in the code to yours.
  3. Click Upload button on Arduino IDE to upload code to ESP32 S3.
  4. Open the Serial Monitor.
  5. Check out the result on Serial Monitor.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
Connecting to WiFi... Connected to WiFi ESP32 S3 Web Server's IP address: 192.168.0.2 ESP32 S3 Web server started
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2
  1. You will see an IP address on the Serial Monitor, for example: 192.168.0.2.
  2. Type the following addresses one-by-one into the address bar of a web browser on your smartphone or PC.
192.168.0.2 192.168.0.2/index.html 192.168.0.2/led.html 192.168.0.2/led.html?state=on 192.168.0.2/led.html?state=off 192.168.0.2/temperature.html 192.168.0.2/blabla.html
  1. Please note that you need to replace 192.168.0.2 with the IP address shown on your Serial Monitor.
  2. You will see the following pages: home page, LED page, temperature page, and Not Found page.
  3. You can also check the output on the Serial Monitor.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
Connecting to WiFi... Connected to WiFi ESP32 S3 Web Server's IP address: 192.168.0.2 ESP32 S3 Web server started Web Server: home page Web Server: LED page Web Server: LED page => turning LED to on Web Server: LED page => turning LED to off Web Server: temperature page Web Server: Not Found
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2
  1. Pro Tip: Bookmark the IP address in your browser for quick access during development. If the IP changes between reboots, consider assigning a static IP in your router's DHCP settings or using mDNS so you can reach the board by hostname.

The previous code uses simple HTML content embedded directly in the sketch. When you want a polished interface with full HTML, CSS, and JavaScript, keeping it all in one file becomes unwieldy. The following section shows how to move each page's HTML into a separate header file, keeping the sketch clean and the HTML easy to edit.

ESP32 S3 Code - Complete multiple-page Web server

The following steps walk you through creating a complete multi-page web server project with separate HTML files for each page. Each HTML file is stored as a C header file that the main sketch includes at compile time.

  1. Open the Arduino IDE.
  2. Create a new sketch and give it a name, for example, ESP32S3WebServer.ino.
  3. Copy the provided code and paste it into that file.
/* * This ESP32 S3 code was developed by newbiely.com * * This ESP32 S3 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3/esp32-s3-web-server-multiple-pages */ /* * This ESP32-S3 code is created by esp32io.com * * This ESP32-S3 code is released in the public domain * * For more detail (instruction and wiring diagram), visit https://esp32io.com/tutorials/esp32-s3-web-server-multiple-pages */ #include <DIYables_ESP32_WebServer.h> #include "index.h" #include "temperature.h" #include "led.h" #include "error_404.h" #define LED_PIN 38 // ESP32-S3 pin GPIO38 connected to LED int LED_state = LOW; float getTemperature() { // YOUR SENSOR IMPLEMENTATION HERE // simulate the temperature value float temp_x100 = random(0, 10000); // a ramdom value from 0 to 10000 return temp_x100 / 100; // return the simulated temperature value from 0 to 100 in float } // WiFi credentials const char WIFI_SSID[] = "YOUR_WIFI_SSID"; // CHANGE IT const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD"; // CHANGE IT // Create web server instance DIYables_ESP32_WebServer server; // Page handlers void handleHome(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { Serial.println("Web Server: home page"); server.sendResponse(client, HTML_CONTENT_HOME); } void handleTemperature(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { Serial.println("Web Server: temperature page"); float temperature = getTemperature(); String html = HTML_CONTENT_TEMPERATURE; // Use the HTML content from the temperature.h file html.replace("%TEMPERATURE_VALUE%", String(temperature)); // update the temperature value server.sendResponse(client, html.c_str()); } void handleLed(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { Serial.println("Web Server: LED page"); // Check for the 'state' parameter in the query string String state = ""; for (int i = 0; i < params.count; i++) { if (String(params.params[i].key) == "state") { state = params.params[i].value; if (state == "on") { LED_state = HIGH; } else if (state == "off") { LED_state = LOW; } // control LED here digitalWrite(LED_PIN, LED_state); Serial.print(" => turning LED to "); Serial.print(state); Serial.println(); break; } } String html = HTML_CONTENT_LED; // Use the HTML content from the led.h file html.replace("%LED_STATE%", LED_state ? "ON" : "OFF"); // update the LED state server.sendResponse(client, html.c_str()); } void handleNotFound(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { // Handle 404 Not Found error Serial.println("Web Server: Not Found"); // Use the HTML content from the error_404.h file server.sendResponse(client, HTML_CONTENT_404); } void setup() { Serial.begin(9600); delay(1000); pinMode(LED_PIN, OUTPUT); Serial.println("ESP32 S3 Web Server"); // Configure routes server.addRoute("/", handleHome); server.addRoute("/temperature.html", handleTemperature); server.addRoute("/led.html", handleLed); // Set custom 404 handler server.setNotFoundHandler(handleNotFound); // Start web server with WiFi connection server.begin(WIFI_SSID, WIFI_PASSWORD); } void loop() { server.handleClient(); }
  1. Change the WiFi information (SSID and password) in the code to yours.
  2. Create the index.h file in Arduino IDE by:
  • Either click on the button just below the serial monitor icon and choose New Tab, or use Ctrl+Shift+N keys.
Arduino IDE 2 adds file
  • Give the file the name index.h and click OK button.
Arduino IDE 2 adds file index.h
  • Copy the below code and paste it into the index.h file.
/* * This ESP32 S3 code was developed by newbiely.com * * This ESP32 S3 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3/esp32-s3-web-server-multiple-pages */ const char *HTML_CONTENT_HOME = R"=====( <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="icon" href="data:,"> <title>Home Page</title> </head> <body> <h1>Welcome to the Home Page</h1> <ul> <li><a href="/led.html">LED Page</a></li> <li><a href="/temperature.html">Temperature Page</a></li> </ul> </body> </html> )=====";
  1. Similarly, create the led.h file in Arduino IDE with the following content.
/* * This ESP32 S3 code was developed by newbiely.com * * This ESP32 S3 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3/esp32-s3-web-server-multiple-pages */ const char *HTML_CONTENT_LED = R"=====( <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="icon" href="data:,"> <title>LED Page</title> </head> <body> <h1>LED Page</h1> <p>LED State: <span style="color: red;">%LED_STATE%</span></p> <a href='/led.html?state=on'>Turn ON</a> <br><br> <a href='/led.html?state=off'>Turn OFF</a> </body> </html> )=====";
  1. Similarly, create the temperature.h file in Arduino IDE with the following content.
/* * This ESP32 S3 code was developed by newbiely.com * * This ESP32 S3 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3/esp32-s3-web-server-multiple-pages */ const char *HTML_CONTENT_TEMPERATURE = R"=====( <!DOCTYPE html> <html> <head> <title>ESP32 S3 - Web Temperature</title> <meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=0.7"> <meta charset="utf-8"> <link rel="icon" href="https://diyables.io/images/page/diyables.svg"> <style> body { font-family: "Georgia"; text-align: center; font-size: width/2pt;} h1 { font-weight: bold; font-size: width/2pt;} h2 { font-weight: bold; font-size: width/2pt;} button { font-weight: bold; font-size: width/2pt;} </style> <script> var cvs_width = 200, cvs_height = 450; function init() { var canvas = document.getElementById("cvs"); canvas.width = cvs_width; canvas.height = cvs_height + 50; var ctx = canvas.getContext("2d"); ctx.translate(cvs_width/2, cvs_height - 80); update_view(%TEMPERATURE_VALUE%); } function update_view(temp) { var canvas = document.getElementById("cvs"); var ctx = canvas.getContext("2d"); var radius = 70; var offset = 5; var width = 45; var height = 330; ctx.clearRect(-cvs_width/2, -350, cvs_width, cvs_height); ctx.strokeStyle="blue"; ctx.fillStyle="blue"; //5-step Degree var x = -width/2; ctx.lineWidth=2; for (var i = 0; i <= 100; i+=5) { var y = -(height - radius)*i/100 - radius - 5; ctx.beginPath(); ctx.lineTo(x, y); ctx.lineTo(x - 20, y); ctx.stroke(); } //20-step Degree ctx.lineWidth=5; for (var i = 0; i <= 100; i+=20) { var y = -(height - radius)*i/100 - radius - 5; ctx.beginPath(); ctx.lineTo(x, y); ctx.lineTo(x - 25, y); ctx.stroke(); ctx.font="20px Georgia"; ctx.textBaseline="middle"; ctx.textAlign="right"; ctx.fillText(i.toString(), x - 35, y); } // shape ctx.lineWidth=16; ctx.beginPath(); ctx.arc(0, 0, radius, 0, 2 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.rect(-width/2, -height, width, height); ctx.stroke(); ctx.beginPath(); ctx.arc(0, -height, width/2, 0, 2 * Math.PI); ctx.stroke(); ctx.fillStyle="#e6e6ff"; ctx.beginPath(); ctx.arc(0, 0, radius, 0, 2 * Math.PI); ctx.fill(); ctx.beginPath(); ctx.rect(-width/2, -height, width, height); ctx.fill(); ctx.beginPath(); ctx.arc(0, -height, width/2, 0, 2 * Math.PI); ctx.fill(); ctx.fillStyle="#ff1a1a"; ctx.beginPath(); ctx.arc(0, 0, radius - offset, 0, 2 * Math.PI); ctx.fill(); temp = Math.round(temp * 100) / 100; var y = (height - radius)*temp/100.0 + radius + 5; ctx.beginPath(); ctx.rect(-width/2 + offset, -y, width - 2*offset, y); ctx.fill(); ctx.fillStyle="red"; ctx.font="bold 34px Georgia"; ctx.textBaseline="middle"; ctx.textAlign="center"; ctx.fillText(temp.toString() + "°C", 0, 100); } window.onload = init; </script> </head> <body> <h1>ESP32 S3 - Web Temperature</h1> <canvas id="cvs"></canvas> </body> </html> )=====";
  1. Similarly, create the error_404.h file in Arduino IDE with the following content.
/* * This ESP32 S3 code was developed by newbiely.com * * This ESP32 S3 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3/esp32-s3-web-server-multiple-pages */ const char *HTML_CONTENT_404 = R"=====( <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="icon" href="data:,"> <title>404 - Page Not Found</title> <style> h1 {color: #ff4040;} </style> </head> <body> <h1>404</h1> <p>Oops! The page you are looking for could not be found on Esp32 Web Server.</p> <p>Please check the URL or go back to the <a href="/">homepage</a>.</p> <p>Or check <a href="https://esp32io.com/tutorials/esp32-web-server-multiple-pages"> Esp32 Web Server</a> tutorial.</p> </body> </html> )=====";
  1. You now have multiple files open in Arduino IDE as shown below:
Arduino IDE 2 multiple files
  1. Click Upload button on Arduino IDE to upload code to ESP32 S3.
  2. Access the web pages of the ESP32 S3 board via a web browser one by one as before. You will see all web pages as shown below:
ESP32 S3 multiple web page

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!