Por qué este proyecto vale la pena
Tienes un ESP32 sirviendo una página web en tu red local: todos los que estén conectados al WiFi pueden abrirla escribiendo la IP. ¿Qué pasa si quieres que solo tú (o quienes tú decidas) puedan ver esa página? La autenticación HTTP básica es la respuesta más simple: un popup pide usuario y contraseña, y sin las credenciales correctas el contenido no se sirve.
En este tutorial vas a montar un servidor web en el ESP32 programado con ESP-IDF (el framework oficial de Espressif) que protege la página con login y permite cerrar sesión cuando quieras.
Antes de empezar
Necesitas:
- Tener instalada la extensión ESP-IDF para VS Code (Visual Studio Code de Microsoft). Si nunca la has usado, busca la guía oficial de Espressif para programar ESP32 con ESP-IDF.
- Una placa de desarrollo ESP32 (cualquier variante: ESP32-WROOM, ESP32-S3, ESP32-C3, todas funcionan).
- Un cable USB para conectar la placa al computador.
Sobre la seguridad
Esto está pensado para tu red local, no para exponer el ESP32 a internet. La autenticación básica HTTP no encripta las credenciales: viajan codificadas en base64, que cualquiera puede revertir. Si alguien ya entró a tu WiFi, esto no lo detiene. Para uso doméstico (evitar que un visitante curioso acceda escribiendo la IP) es más que suficiente.
Qué va a hacer el proyecto

- Cuando alguien intenta abrir la IP del ESP32, aparece un popup pidiendo usuario y contraseña.
- Si las credenciales son correctas, se muestra la página principal con un botón Logout.
- Al hacer clic en Logout, eres redirigido a una página de cierre de sesión y luego al login otra vez.
- Cada dispositivo nuevo (notebook, celular, tablet) que abra la página tiene que loguearse por su cuenta.
Paso 1: Crea un proyecto ESP-IDF desde cero
La extensión de VS Code tiene un asistente para generar la estructura del proyecto.
- Abre el panel ESP-IDF Espressif en la barra lateral.
- Expande Advanced y haz clic en New Project Wizard.
- Elige la versión del framework: ESP-IDF v5.4.1.

En la siguiente ventana:
- Project Name: el nombre que quieras.
- Project Directory: una carpeta local. NO uses carpetas de Google Drive, OneDrive o Dropbox, porque la compilación crea miles de archivos y la sincronización en la nube vuelve todo lentísimo.
- ESP-IDF Target: el chip de tu placa (yo uso
esp32s3). - ESP-IDF Board: la configuración correspondiente al chip (para S3 elige ESP32-S chip via builtin USB-JTAG).
- Serial Port: el COM/tty donde está conectada la placa.
- Choose Template: presiona el botón azul para crear desde plantilla.

Selecciona ESP-IDF Templates → sample project y presiona Create project using template sample project.

Cuando aparezca la notificación, haz clic en Open Project y VS Code abrirá el proyecto recién creado.

Solo vas a editar main/main.c. El resto de archivos (CMakeLists, sdkconfig, etc.) puedes dejarlos como están.
Paso 2: Configura SSID y contraseña con Kconfig.projbuild
En vez de hardcodear las credenciales WiFi dentro del .c, lo limpio es
exponerlas en el SDK Configuration Editor (menuconfig). Para eso necesitas
crear un archivo Kconfig.projbuild dentro de la carpeta main/.
Click derecho sobre main → New File → nómbralo exactamente Kconfig.projbuild y pega:
menu "Wi-Fi Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
help
SSID (network name) you want to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
help
Password of the Wi-Fi network you want to connect to.
endmenu

Guarda. Ahora abre el SDK Configuration Editor (ícono del engranaje en la barra inferior), busca "wifi", llena los campos WiFi SSID y WiFi Password con tus credenciales reales y presiona Save.

Esto crea variables CONFIG_ESP_WIFI_SSID y CONFIG_ESP_WIFI_PASSWORD que el código va a leer en tiempo de compilación.
Paso 3: El código del servidor web con autenticación
Copia este código completo a main/main.c. Reemplaza el usuario/contraseña en los #define con los que quieras:
/*
Adaptado de Random Nerd Tutorials
https://RandomNerdTutorials.com/esp-idf-esp32-web-server-http-authentication/
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <esp_log.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_netif.h>
#include <esp_http_server.h>
#include <esp_tls.h>
#include <esp_check.h>
#include <nvs_flash.h>
#include "esp_tls_crypto.h"
#include "sdkconfig.h"
#define MY_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define MY_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
#define BASIC_AUTH_USER "admin"
#define BASIC_AUTH_PASS "password"
typedef struct {
char *username;
char *password;
} basic_auth_info_t;
#define HTTPD_401 "401 UNAUTHORIZED"
static const char *TAG = "web_server";
// HTML servido en la raíz "/"
static const char *root_page =
"<!DOCTYPE html><html><head>"
"<title>ESP-IDF: ESP32 Web Server</title>"
"<meta name="viewport" content="width=device-width, initial-scale=1">"
"</head><body>"
"<h1>ESP-IDF: ESP32 Web Server</h1>"
"<p>Hola desde el ESP32!</p>"
"<button onclick="window.location.href='/logout';">Logout</button>"
"</body></html>";
// HTML servido en "/logout" (fuerza al navegador a olvidar credenciales)
static const char *logout_page =
"<!DOCTYPE html><html><head>"
"<title>Logout</title>"
"<script>"
"function forceLogout(){"
" var xhr = new XMLHttpRequest();"
" xhr.open('GET', '/', true, 'logout', 'logout');"
" xhr.send();"
" setTimeout(function(){ window.location.href='/'; }, 500);"
"}"
"</script></head>"
"<body onload='forceLogout()'><h2>Cerrando sesion...</h2></body></html>";
// Devuelve 401 + header WWW-Authenticate para gatillar el popup del navegador
static esp_err_t send_401_response(httpd_req_t *req)
{
httpd_resp_set_status(req, HTTPD_401);
httpd_resp_set_type(req, "text/html");
httpd_resp_set_hdr(req, "Connection", "keep-alive");
httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm="ESP32 Web Server"");
httpd_resp_send(req, NULL, 0);
return ESP_OK;
}
// Construye el string "Basic base64(user:pass)" que el cliente debe enviar
static char *http_auth_basic(const char *username, const char *password)
{
size_t out;
char *user_info = NULL;
char *digest = NULL;
size_t n = 0;
int rc = asprintf(&user_info, "%s:%s", username, password);
if (rc < 0 || !user_info) {
ESP_LOGE(TAG, "No se pudo armar user_info");
return NULL;
}
esp_crypto_base64_encode(NULL, 0, &n,
(const unsigned char *)user_info, strlen(user_info));
digest = calloc(1, 6 + n + 1);
if (digest) {
strcpy(digest, "Basic ");
esp_crypto_base64_encode((unsigned char *)digest + 6, n, &out,
(const unsigned char *)user_info, strlen(user_info));
}
free(user_info);
return digest;
}
// Compara el header Authorization que llegó contra el esperado
static bool check_basic_auth(httpd_req_t *req, const char *user, const char *pass)
{
size_t buf_len = httpd_req_get_hdr_value_len(req, "Authorization") + 1;
if (buf_len <= 1) return false;
char *buf = calloc(1, buf_len);
if (!buf) return false;
if (httpd_req_get_hdr_value_str(req, "Authorization", buf, buf_len) != ESP_OK) {
free(buf);
return false;
}
char *expected = http_auth_basic(user, pass);
if (!expected) { free(buf); return false; }
bool ok = (strcmp(expected, buf) == 0);
free(expected); free(buf);
if (ok) ESP_LOGI(TAG, "Usuario autenticado: %s", user);
else ESP_LOGE(TAG, "Autenticacion fallida");
return ok;
}
static esp_err_t logout_get_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "text/html");
httpd_resp_send(req, logout_page, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
static const httpd_uri_t logout_uri = {
.uri = "/logout", .method = HTTP_GET,
.handler = logout_get_handler, .user_ctx = NULL
};
static esp_err_t basic_auth_get_handler(httpd_req_t *req)
{
basic_auth_info_t *info = req->user_ctx;
if (!check_basic_auth(req, info->username, info->password)) {
return send_401_response(req);
}
httpd_resp_set_type(req, "text/html");
httpd_resp_send(req, root_page, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
static httpd_uri_t basic_auth = {
.uri = "/", .method = HTTP_GET, .handler = basic_auth_get_handler,
};
static void httpd_register_basic_auth(httpd_handle_t server)
{
basic_auth_info_t *info = calloc(1, sizeof(basic_auth_info_t));
if (info) {
info->username = BASIC_AUTH_USER;
info->password = BASIC_AUTH_PASS;
basic_auth.user_ctx = info;
httpd_register_uri_handler(server, &basic_auth);
}
}
static httpd_handle_t start_web_server(void)
{
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_handle_t server = NULL;
if (httpd_start(&server, &config) == ESP_OK) {
ESP_LOGI(TAG, "Servidor HTTP iniciado en el puerto %d", config.server_port);
httpd_register_uri_handler(server, &logout_uri);
httpd_register_basic_auth(server);
return server;
}
ESP_LOGE(TAG, "No se pudo iniciar el servidor HTTP");
return NULL;
}
Importante: este snippet contiene los handlers y el setup del servidor. Te falta el manejo de WiFi (
wifi_event_handler,wifi_init_sta) y elapp_main()que une todo. Toma el código completo desde el repo del autor original (link al final).
Paso 4: Compila y flashea
Con el código pegado y el menuconfig listo:
- En la barra inferior de VS Code, presiona el ícono del rayo (Flash) o usa el comando ESP-IDF: Build, Flash and Monitor.
- La primera compilación tarda varios minutos (descarga toolchains y compila todo el SDK).
- Cuando termine de flashear, el monitor serial abre automáticamente.
Paso 5: Probando el servidor
En el monitor serial vas a ver una línea como:
I (4521) wifi_station: got ip:192.168.1.42
I (4523) web_server: Servidor HTTP iniciado en el puerto 80
Anota la IP. Abre tu navegador en cualquier dispositivo de la misma red
WiFi y entra a http://192.168.1.42 (la que te tocó a ti).
Aparece el popup pidiendo credenciales. Ingresa:
- Usuario:
admin - Contraseña:
password
(O lo que hayas puesto en los #define BASIC_AUTH_USER/PASS).
Si todo funciona ves la página con el botón Logout. Al hacer clic, eres
redirigido a /logout, el JS local rompe la sesión y vuelves al popup.
Cómo extenderlo
- Múltiples usuarios: en vez de
BASIC_AUTH_USER/PASSfijos, guarda una lista en NVS y compárala iterando. - Páginas dinámicas: en el
root_pagereemplaza el HTML estático por un buffer que construyas consnprintfincluyendo lecturas de sensores. - HTTPS: ESP-IDF soporta
esp_https_server, la migración es directa pero necesitas certificado autofirmado.
Listo. Tu ESP32 ahora sirve contenido protegido en tu red local.
Recursos
- Tutorial original (inglés): ESP-IDF: ESP32 Web Server con autenticación HTTP básica
- Código completo
main.c(incluye el manejo de WiFi yapp_main()): wifi_web_server_http_auth.c - Documentación oficial ESP-IDF: esp_http_server
Versión chilena con componentes en stock local en MechatronicStore.





