My ESP32 HTTPClient GET request always returns 204 or fails

HTTPClient always uses hostByName, but you can try IPAddress directly with WiFiClient/NetworkClient and manually perform HTTP. It does connect to that bogus IP and return the 204 with all those headers.

Replace this part of your original code

  // wait for WiFi connection
  if (WiFi.status() == WL_CONNECTED) {

    NetworkClient net;
    // IPAddress ip{ 18, 208, 113, 193 };
    IPAddress ip{ 3, 233, 51, 125 };

    Serial.println("building GET...");

    if (!net.connect(ip, 80)) {
      Serial.println("failed to connect");
      for (;;);
    }
    Serial.print("remote IP: "); Serial.println(net.remoteIP());
    net.println("GET /get HTTP/1.1");
    net.println("Accept: */*");
    net.println("Accept-Language: en-US,en;q=0.9");
    net.println("Host: httpbin.org");
    net.println("User-Agent: ESP32");

    Serial.println("sending GET...");
    net.println("Connection: close");
    net.println();

    while (net.connected() || net.available()) {
      if (net.available()) {
        char c = net.read();
        Serial.print(c);
      }
    }
    net.stop();
  }

@kenb4
failed to connect well, that's great. maybe my ESP32's broken?

And if you switch the commented-out IP to go to the bogus address, that works?

It does work, but I only get 204. Not only that, but my computer can't even ping that address. It doesn't mean that that address is non-existent, which my ESP32 shows otherwise. But something weird is going on here. And not all IP addresses can be pinged. Well, I mean, they can be, but they don't expect pinging, so to requests always time out.

building GET...

remote IP: 18.208.113.193

sending GET...

HTTP/1.1 204 No Content

Server: nginx

Date: Fri, 25 Jul 2025 07:30:25 GMT

Connection: close

Etag: W/"a-bAsFyilMr4Ra1hIU5PyoyFRunpI"

Nel: {"report_to":"heroku-nel","response_headers":["Via"],"max_age":3600,"success_fraction":0.01,"failure_fraction":0.1}

Report-To: {"group":"heroku-nel","endpoints":[{"url":"https://nel.heroku.com/reports?s=zl66qQoBbQr5a8uh%2Fm3yvO19Ereq9VXdrd%2B0RKUpJ48%3D\u0026sid=67ff5de4-ad2b-4112-9289-cf96be89efed\u0026ts=1753428624"}],"max_age":3600}

Reporting-Endpoints: heroku-nel="https://nel.heroku.com/reports?s=zl66qQoBbQr5a8uh%2Fm3yvO19Ereq9VXdrd%2B0RKUpJ48%3D&sid=67ff5de4-ad2b-4112-9289-cf96be89efed&ts=1753428624"

Via: 1.0 heroku-router

X-Powered-By: Express

I meant to say I get the same kind of response. Excluding the request headers, I get 403 instead. (Using HTTP/1.0 means that Host is not required. 204 is No Content, so that has no response body.)

$ telnet 18.208.113.193 80
Trying 18.208.113.193...
Connected to ec2-18-208-113-193.compute-1.amazonaws.com.
Escape character is '^]'.
GET /get HTTP/1.0

HTTP/1.1 403 Forbidden
Server: nginx
Date: Fri, 25 Jul 2025 07:37:19 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 706
Connection: close
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Etag: W/"2c2-197108c4670"
Last-Modified: Tue, 27 May 2025 07:01:58 GMT
Nel: {"report_to":"heroku-nel","response_headers":["Via"],"max_age":3600,"success_fraction":0.01,"failure_fraction":0.1}
Report-To: {"group":"heroku-nel","endpoints":[{"url":"https://nel.heroku.com/reports?s=%2B218KFUsRZw3%2BZ0fsDic%2BKY%2BOQ6XHnFyBaWbm%2FU1FUA%3D\u0026sid=67ff5de4-ad2b-4112-9289-cf96be89efed\u0026ts=1753429039"}],"max_age":3600}
Reporting-Endpoints: heroku-nel="https://nel.heroku.com/reports?s=%2B218KFUsRZw3%2BZ0fsDic%2BKY%2BOQ6XHnFyBaWbm%2FU1FUA%3D&sid=67ff5de4-ad2b-4112-9289-cf96be89efed&ts=1753429039"
Via: 1.0 heroku-router
X-Powered-By: Express

<!doctype html>
<html>
  <head>
    <link
      rel="stylesheet"
      href="http://dtjgrt9acqi82.cloudfront.net/styles.css"
    />
  </head>
  <body>
    <div class="center">
      <p class="vertical-center">
        <img
          src="http://dtjgrt9acqi82.cloudfront.net/cpGfxInternetFrozen.png"
          class="img-media-small"
        />
        <img
          src="http://dtjgrt9acqi82.cloudfront.net/cpGfxInternetFrozen@2x.png"
          class="img-media-medium"
        />
        <img
          src="http://dtjgrt9acqi82.cloudfront.net/cpGfxInternetFrozen@3x.png"
          class="img-media-large"
        />
        <br />
        Internet access frozen.
      </p>
    </div>
  </body>
</html>
Connection closed by foreign host.

I also have to be quick, otherwise it just closes right away. Seemed to have more time when I tried it yesterday.

If an ESP8266 (and your desktop browser) on the same network work just fine; but with the ESP32 the DNS resolves to the only hard-coded IP that you're allowed to connect to, that starts to look like a targeted attack on the core platform code or the hardware.

As I mentioned, I'm running version 3.0.7. You could try downgrading to that.

I already tried downgrading to that, it didn't work.

Also, my ESP32 works very well as a captive portal, which is objectively harder, so I guess it's not broken, huh? But it's not functioning as a client that well. It keeps getting redirected, I guess, or something.

If you've got an hour, try some random IPs with this replacement loop

struct Counter {
  unsigned count = 0;
  IPAddress last {};
} count[6];

void loop() {
  NetworkClient net;
  IPAddress ip, rip(random(1, 255), random(1, 255), random(1, 255), random(1, 255));

  if (Serial.available()) {
    Serial.print("---"); Serial.print('\t'); Serial.println(millis());
    String line = Serial.readStringUntil('\n');
    line.trim();
    if (line.isEmpty()) {
      ip = rip;
    } else if (!ip.fromString(line)) {
      Serial.print("bad input:"); Serial.print('\t'); Serial.println(line);
      ip = rip;
    }
    unsigned i = 0;
    for (Counter &c : count) {
      Serial.print(i); Serial.print(i++ < 2 ? ".." : "xx");
      Serial.print('\t'); Serial.print(c.count);
      Serial.print('\t'); Serial.println(c.last);
    }
    while (Serial.available()) Serial.read();
    Serial.println("---");
  } else {
    ip = rip;
  }
  Serial.print(ip);

  if (!net.connect(ip, 80)) {
    count[0].count++;
    count[0].last = ip;
    Serial.print('\t'); Serial.println(count[0].count);
    return;
  }
  net.println("GET /favicon.ico HTTP/1.0");
  net.println();

  while (net.connected() || net.available()) {
    if (net.available()) {
      String line = net.readStringUntil('\n');
      int status = line.indexOf(' ');
      if (status > 0) {
        line.remove(0, status);
        line.trim();
        status = line.toInt();
      }
      Serial.print('\t'); Serial.print(status);
      status /= 100;
      if (status < 2 || status > 5) {
        status = 1;
      }
      count[status].count++;
      count[status].last = ip;
      Serial.print('\t'); Serial.println(count[status].count);
      break;
    }
  }
  while (net.available()) net.read();
  net.stop();
}

I got about a 1% hit rate. You can press Enter at any time for it to print the latest count/hit (after the next connect attempt); with an optional IP to try directly, including the bogus one that does.

---	3146209
0..	1049	222.44.97.223
1..	0	0.0.0.0
2xx	1	50.200.65.25
3xx	3	159.65.67.208
4xx	9	211.43.220.246
5xx	0	0.0.0.0

Okay, thanks. I'll try that.

starting in... 10... 9... 8... 7... 6... 5... 4... 3... 2... 1...
245.13.29.226	1
50.73.172.138	2
133.47.137.99	3
52.156.201.185	4
50.171.132.244	5
187.122.34.219	6
211.242.211.70	7
52.89.54.211	8
84.163.18.233	9
99.145.40.202	10
251.89.240.233	11
201.185.102.38	12
79.26.131.57	13
79.198.66.233	14
190.239.9.123	15
15.168.194.107	16
171.4.70.143	17
247.73.35.7	18
158.81.191.213	19
118.134.100.123	20
248.239.160.188	21
9.43.155.250	22
95.141.30.1	23
163.51.166.58	24
188.34.97.62	25
97.118.140.37	26
204.47.150.187	27
219.227.27.247	28
224.80.113.33	29
136.25.80.201	30
54.34.62.249	31
92.215.36.69	32
202.196.233.222	33
219.180.87.203	34
31.189.213.78	35
75.180.158.41	36
179.206.137.179	37
168.37.45.128	38
167.208.2.1	39
5.187.150.129	40
212.143.236.95	41
31.21.16.120	42
113.153.127.114	43
98.91.107.200	44
61.124.9.4	45
184.165.206.191	46
162.47.178.11	47
1.217.105.71	48
155.237.82.100	49
65.236.39.147	50
8.103.218.163	51
203.50.24.98	52
240.149.10.187	53
136.230.94.223	54
11.17.92.76	55
135.17.165.228	56
3.131.153.180	57
121.146.129.248	58
191.116.177.185	59
234.167.83.89	60
199.166.51.49	61
182.140.123.168	62
106.102.113.17	63
74.194.80.130	64
229.110.77.45	65
204.200.132.26	66
66.244.167.38	67
119.197.209.44	68
242.181.169.186	69
79.198.246.159	70
68.173.56.209	71
200.205.168.199	72
25.207.141.75	73
138.83.49.236	74
92.104.64.13	75
200.254.154.111	76
161.35.203.124	77
249.33.231.72	78
243.160.28.233	79
15.146.56.49	80
244.126.210.224	81
135.51.32.221	82
241.251.182.175	83
60.162.206.67	84
201.202.30.170	85
137.182.57.134	86
91.98.197.149	87
89.184.212.108	88
58.66.156.132	89
84.84.28.164	90
234.129.80.202	91
123.178.117.56	92
70.130.28.238	93
140.165.132.24	94
91.136.124.99	95
16.154.208.161	96
213.232.143.240	97
187.129.58.79	98
220.188.146.22	99
20.131.110.220	100
172.210.169.248	101
107.2.117.80	102
15.184.215.184	103
47.89.171.201	104
112.142.232.92	105
250.167.160.21	106
67.195.57.85	107
79.157.139.200	108
18.186.127.225	109
39.106.116.35	110
193.50.192.112	111
130.203.219.204	112
7.81.9.59	113
87.64.34.169	114
183.146.10.207	115
155.217.168.173	116
251.1.33.85	117
32.9.244.221	118
244.75.127.226	119
14.47.145.132	120
221.32.195.67	121
44.6.29.203	122
143.55.18.194	123

My god!

Oh yeah, a 1% hit rate.

Been 3h, still nothing...

So the last random ones that worked for me, returning any normal-range HTTP status code, instead of not connecting at all; you can try typing each of those three IPs in.

Double-check with a laptop or phone browser on the same WiFi. Be sure to use plain http:// on port 80 instead of https: And then the magic-bogus 18.208.113.193

So after three hours, it has done maybe a few thousand, and if it still doesn't connect to anything but that one particular IP: that would be suspicious.

Try taking the ESP32, ESP8266, and a laptop (with the IDE installed) to another WiFi, like at the coffee shop or library.

Yeah, it has done about 5000 at this point.

Also, browsers just said that they aren't safe, and I also had my computer ping them, so here's the results:

Microsoft Windows [Version 10.0.26100.4652]
(c) Microsoft Corporation. All rights reserved.

C:\Users\Logan>ping 50.200.65.25

Pinging 50.200.65.25 with 32 bytes of data:
Request timed out.
Request timed out.
Request timed out.
Request timed out.

Ping statistics for 50.200.65.25:
    Packets: Sent = 4, Received = 0, Lost = 4 (100% loss),

C:\Users\Logan>ping 159.65.67.208

Pinging 159.65.67.208 with 32 bytes of data:
Reply from 159.65.67.208: bytes=32 time=665ms TTL=35
Reply from 159.65.67.208: bytes=32 time=103ms TTL=35
Reply from 159.65.67.208: bytes=32 time=102ms TTL=35
Reply from 159.65.67.208: bytes=32 time=103ms TTL=35

Ping statistics for 159.65.67.208:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 102ms, Maximum = 665ms, Average = 243ms

C:\Users\Logan>ping 211.43.220.246

Pinging 211.43.220.246 with 32 bytes of data:
Reply from 211.43.220.246: bytes=32 time=336ms TTL=101
Reply from 211.43.220.246: bytes=32 time=229ms TTL=101
Reply from 211.43.220.246: bytes=32 time=228ms TTL=101
Reply from 211.43.220.246: bytes=32 time=230ms TTL=101

Ping statistics for 211.43.220.246:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 228ms, Maximum = 336ms, Average = 255ms

C:\Users\Logan>

Nowadays, the only "secure contexts" require https:, aside from http://localhost. But they do have to connect for the browser to declare them unsafe, right? On Windows, I believe PowerShell uses curl as an alias for something. Not sure if you try it on the good old Command Prompt (unless you have curl.exe on the path somewhere, of course)

$ curl 211.43.220.246/favicon.ico
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Not Found</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Not Found</h2>
<hr><p>HTTP Error 404. The requested resource is not found.</p>
</BODY></HTML>

Indeed, it does have to connect to a browser for them to declare them unsafe.

I'm just gonna see if changing my ESP32's local IP will do anything.

Nope, headers?

Nope.

TCP reuse is now set to false.

Hmm... Still not working, ESP8266 still does work.