Articles / Embedded

  • esp8266
  • arduino
  • wifi
  • soft-ap
  • networking
  • debugging

ESP8266 Soft AP Gives Out IPs but the Web Page Won't Load

The symptom is maddening: your phone joins the ESP8266's access point, gets an address in 192.168.4.x, and then http://192.168.4.1/ just spins. DHCP working proves the radio and the lease server are fine. It says nothing about whether your HTTP traffic is going where you think.

Usually the ESP is innocent and the client is sending the request somewhere else. So first, prove which side is broken.

Test with a laptop before you blame the firmware

A phone is the worst debugging tool here, because it has opinions. Join the AP from a laptop instead and run:

ip addr show wlan0
ping -c 3 192.168.4.1
curl -v http://192.168.4.1/
  • curl gets a response: the firmware is fine, the phone is the problem.
  • Ping works but curl hangs: look at the sketch.
  • Ping fails too: look at subnets, below.

A minimal sketch that behaves

Start small so your own handlers are ruled out. This one sets an explicit subnet, which matters shortly.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

const IPAddress apIP(192, 168, 50, 1);
const IPAddress mask(255, 255, 255, 0);
ESP8266WebServer server(80);

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(apIP, apIP, mask);
  WiFi.softAP("esp-test", "change-me-please");
  server.on("/", [] { server.send(200, "text/plain", "hello\n"); });
  server.begin();
  Serial.println(WiFi.softAPIP());
}

void loop() {
  server.handleClient();
}

Two things are easy to miss. handleClient() must be called constantly, so anything that blocks loop() makes the page hang while DHCP (handled by the network stack) carries on happily. And the WPA2 passphrase needs at least eight characters.

Cause one: the phone sends your request over mobile data

Phones check whether a Wi-Fi network has internet access. Yours does not, so Android in particular marks it "connected, no internet" and may route traffic over the mobile connection instead. Your request for 192.168.4.1 then goes out through the carrier, which has never heard of it, and times out.

Quick detour, because this is the interesting part. The check is an HTTP request to a well-known probe URL. A normal network answers it; yours answers nothing (or the DNS lookup fails), so the OS concludes the network is useless and quietly demotes it. The AP is working exactly as designed; the phone is being helpful.

Fixes, in order of laziness:

  1. Accept the "keep Wi-Fi connected" prompt if the phone shows one.
  2. Turn mobile data off while testing.
  3. Forget the network and rejoin, then choose to stay connected.
  4. Make the ESP pass the probe (next section).

Making the phone treat the AP as a captive portal

If end users will use this, do not ask them to turn off mobile data. The standard trick is a DNS server on the ESP that answers every name with its own address, so probes land on your web server and the OS shows a "sign in" page.

#include <DNSServer.h>

DNSServer dns;

// in setup(), after softAP():
dns.start(53, "*", apIP);

// in loop():
dns.processNextRequest();

Your server should then answer unknown paths with a redirect to http://192.168.50.1/, since the probes request their own paths. I have not tested every Android and iOS version against this, and behaviour differs between them, so test on the devices you care about.

Cause two: the subnets collide

With WIFI_AP_STA the ESP is a client of your home router and an access point at once. The default soft AP subnet is 192.168.4.0/24. If the router also hands out 192.168.4.x, the ESP has two interfaces on the same network and replies go out the wrong one.

That is why the sketch above picks 192.168.50.0/24. Choose any private range your upstream network does not use. Call softAPConfig() before softAP(), as the core's own examples do.

Cause three: the URL is not what you think

  • Use the IP, not a hostname. mDNS names like esp.local often fail on Android.
  • Browsers that default to HTTPS may try port 443; type http:// explicitly.
  • Check the port in ESP8266WebServer server(80) matches what you type.
  • Print WiFi.softAPgetStationNum() to the serial port to confirm the client is really associated.

Reading the result

If the laptop works and the phone does not, you have a client routing problem, not a firmware bug. If neither works, the sketch above on a clean subnet narrows it to your handler code or a blocked loop. Either way, the DHCP lease was never evidence that HTTP would work.