user89147: I've set up an esp32 board (wemos d1 r32) to send a request to a server hosted on my local network. If I set up a very basic go server, the board handles the response fine (200 with the appropriate body content). Once I switch to a Node JS Server, it only responds with a 400 status code. Weirdest of all none of the console.log calls in the route handlers get called, which indicates that it quits before it even gets to the express server. I've tested that the server returns the body & status code I want using postman, all looks good.
I've tried different frameworks (Koa) to see if this is related to Express, but it doesn't work with that either. I assume its something to do with NodeJS?
Express server
import express from "express";

const app = express();
app.use(express.json());
app.post("/", (req, res) => {
  console.log(req.body);
  req.statusCode = 200;
  res.send("200");
});

app.get("/hello", (req, res) => {
  res.setTimeout(50000);
  console.log(req.body);
  res.setHeader("Location", "123");
  res.setHeader("Server", "123");
  req.statusCode = 200;
  res.send("200");
});

app.listen(8090, () => {
  console.log(`Example app listening on port ${8090}`);
});

Go server
package main

import (
    "fmt"
    "net/http"
)

func hello(w http.ResponseWriter, req *http.Request) {
    for name, headers := range w.Header() {
        for _, h := range headers {
            fmt.Fprintf(w, "%v: %v\n", name, h)
        }
    }
    fmt.Fprintf(w, "hello\n")
}

func headers(w http.ResponseWriter, req *http.Request) {

    for name, headers := range req.Header {
        for _, h := range headers {
            fmt.Fprintf(w, "%v: %v\n", name, h)
        }
    }
}

func main() {
    fmt.Println("?")

    http.HandleFunc("/hello", hello)
    http.HandleFunc("/headers", headers)

    http.ListenAndServe(":8090", nil)
}

Arduino script
#include <HTTPClient.h>
#include <WiFi.h>

// #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "esp_log.h"

const char* ssid = "xxx";
const char* password = "xxx";

String webserver = "http://192.168.0.19:8090/hello";

String qrCode;

void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info){
  Serial.println("Connected to AP successfully!");
}

void WiFiGotIP(WiFiEvent_t event, WiFiEventInfo_t info){
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info){
  
  WiFi.disconnect(true);
  delay(1000);
  WiFi.mode(WIFI_STA);
  delay(1000);
  Serial.println("Disconnected from WiFi access point");
  Serial.print("WiFi lost connection. Reason: ");
  Serial.println(info.disconnected.reason);
  Serial.println("Trying to Reconnect");
  WiFi.begin(ssid, password);
  delay(5000);
}

void setup() {
  esp_log_level_set("*", ESP_LOG_VERBOSE);
  Serial.begin(9600);
  Serial2.begin(9600);
  Serial.println("Hello, ESP32!");
  Serial.println(WiFi.getMode());
  Serial.println(WiFi.macAddress());

  WiFi.onEvent(WiFiStationConnected, WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED);
  WiFi.onEvent(WiFiGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
  WiFi.onEvent(WiFiStationDisconnected, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);

  WiFi.begin(ssid, password);
}

int cnt = 0;

void loop() {
  while (Serial2.available()) {
    int  ch = Serial2.read();
    char character = ch;
    Serial.print(ch);

    // end char
    if(character == 13){
      Serial.print("\n");
      Serial.println(qrCode);
      Serial.println(cnt);
      qrCode = "";
      cnt = 0;
      send(qrCode);
    } else {
      cnt = ch + cnt;
      qrCode = qrCode + character;
    }
  }
}

void tryWifi() {
  WiFi.begin(ssid, password);
  int status = WiFi.status();
  while (status == 6 || status == 4 || status == 5 || status == 1) {
    Serial.print("Failed to connect:"); 
    Serial.print(status);
    Serial.print("\n");
    delay(1500);
    WiFi.begin(ssid, password);
  }
}

void send(String json) {
  HTTPClient http; 

  if(http.begin(webserver)){    

      int httpCode = http.GET();
      http.addHeader("Content-Type", "application/json");
      http.addHeader("Host", "something");
      // client.print("Content-Length: ");
      delay(1500);  
      
      Serial.println(httpCode);
      Serial.println(http.getString());
      if (httpCode > 0) {
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
        if(httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          String payload = http.getString();                 
          if (payload != "false"){
            Serial.println(payload); 
            delay(1500);  
          } else {
          Serial.println("Bad Playload");
          }
        } else {
          
        }
      } else {
      Serial.println("Error sending to HTTP");
      }
      http.end(); 
    } else {
      Serial.println("?");
    } 
}


user89147: I've figured out after an unreasonable amount of time that the request fails with a 400 as it doesn't have a slash at the end of the URL (http://192.168.1.208:8090 -> http://192.168.1.208:8090/). This must be something specific to NodeJS, as it works ok on a go server.

user46852: Your Arduino code reads like this:
  int httpCode = http.GET();
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Host", "something");
  // client.print("Content-Length: ");
  delay(1500);  
  
  Serial.println(httpCode);
  Serial.println(http.getString());
  if (httpCode > 0) {

The call http.GET() actually performs the HTTP request. You're adding headers after you perform the request. They need to be added before it. So your code should read:
  // http.addHeader("Content-Type", "application/json");
  int httpCode = http.GET();

  // delay(1500);   -- there's no need to call delay() here
  
  Serial.println(httpCode);
  Serial.println(http.getString());
  if (httpCode > 0) {

HTTP status code 400 means "bad request". Some web servers will return this code if headers that they expect to see are missing or have non-sensical values. The Go and Javascript web servers are different implementations and may behave differently with malformed requests.
I've removed "Host" header as HTTPClient does this for you. And there's no need for the delay() call after calling http.GET().
Edit: I also commented out the "Content-Type" header as it makes no sense on a GET request, thanks to @EdgarBonet for mentioning that. I'm leaving it there as a comment to make it clear that that any headers should be added before the request is performed.

89147: <p>I've set up an esp32 board (wemos d1 r32) to send a request to a server hosted on my local network. If I set up a very basic go server, the board handles the response fine (200 with the appropriate body content). Once I switch to a Node JS Server, it only responds with a <code>400</code> status code. Weirdest of all none of the <code>console.log</code> calls in the route handlers get called, which indicates that it quits before it even gets to the express server. I've tested that the server returns the body &amp; status code I want using postman, all looks good.</p>
<p>I've tried different frameworks (Koa) to see if this is related to Express, but it doesn't work with that either. I assume its something to do with NodeJS?</p>
<p><strong>Express server</strong></p>
<pre><code>import express from &quot;express&quot;;

const app = express();
app.use(express.json());
app.post(&quot;/&quot;, (req, res) =&gt; {
  console.log(req.body);
  req.statusCode = 200;
  res.send(&quot;200&quot;);
});

app.get(&quot;/hello&quot;, (req, res) =&gt; {
  res.setTimeout(50000);
  console.log(req.body);
  res.setHeader(&quot;Location&quot;, &quot;123&quot;);
  res.setHeader(&quot;Server&quot;, &quot;123&quot;);
  req.statusCode = 200;
  res.send(&quot;200&quot;);
});

app.listen(8090, () =&gt; {
  console.log(`Example app listening on port ${8090}`);
});
</code></pre>
<p><strong>Go server</strong></p>
<pre><code>package main

import (
    &quot;fmt&quot;
    &quot;net/http&quot;
)

func hello(w http.ResponseWriter, req *http.Request) {
    for name, headers := range w.Header() {
        for _, h := range headers {
            fmt.Fprintf(w, &quot;%v: %v\n&quot;, name, h)
        }
    }
    fmt.Fprintf(w, &quot;hello\n&quot;)
}

func headers(w http.ResponseWriter, req *http.Request) {

    for name, headers := range req.Header {
        for _, h := range headers {
            fmt.Fprintf(w, &quot;%v: %v\n&quot;, name, h)
        }
    }
}

func main() {
    fmt.Println(&quot;?&quot;)

    http.HandleFunc(&quot;/hello&quot;, hello)
    http.HandleFunc(&quot;/headers&quot;, headers)

    http.ListenAndServe(&quot;:8090&quot;, nil)
}
</code></pre>
<p><strong>Arduino script</strong></p>
<pre><code>#include &lt;HTTPClient.h&gt;
#include &lt;WiFi.h&gt;

// #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include &quot;esp_log.h&quot;

const char* ssid = &quot;xxx&quot;;
const char* password = &quot;xxx&quot;;

String webserver = &quot;http://192.168.0.19:8090/hello&quot;;

String qrCode;

void WiFiStationConnected(WiFiEvent_t event, WiFiEventInfo_t info){
  Serial.println(&quot;Connected to AP successfully!&quot;);
}

void WiFiGotIP(WiFiEvent_t event, WiFiEventInfo_t info){
  Serial.println(&quot;WiFi connected&quot;);
  Serial.println(&quot;IP address: &quot;);
  Serial.println(WiFi.localIP());
}

void WiFiStationDisconnected(WiFiEvent_t event, WiFiEventInfo_t info){
  
  WiFi.disconnect(true);
  delay(1000);
  WiFi.mode(WIFI_STA);
  delay(1000);
  Serial.println(&quot;Disconnected from WiFi access point&quot;);
  Serial.print(&quot;WiFi lost connection. Reason: &quot;);
  Serial.println(info.disconnected.reason);
  Serial.println(&quot;Trying to Reconnect&quot;);
  WiFi.begin(ssid, password);
  delay(5000);
}

void setup() {
  esp_log_level_set(&quot;*&quot;, ESP_LOG_VERBOSE);
  Serial.begin(9600);
  Serial2.begin(9600);
  Serial.println(&quot;Hello, ESP32!&quot;);
  Serial.println(WiFi.getMode());
  Serial.println(WiFi.macAddress());

  WiFi.onEvent(WiFiStationConnected, WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED);
  WiFi.onEvent(WiFiGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
  WiFi.onEvent(WiFiStationDisconnected, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);

  WiFi.begin(ssid, password);
}

int cnt = 0;

void loop() {
  while (Serial2.available()) {
    int  ch = Serial2.read();
    char character = ch;
    Serial.print(ch);

    // end char
    if(character == 13){
      Serial.print(&quot;\n&quot;);
      Serial.println(qrCode);
      Serial.println(cnt);
      qrCode = &quot;&quot;;
      cnt = 0;
      send(qrCode);
    } else {
      cnt = ch + cnt;
      qrCode = qrCode + character;
    }
  }
}

void tryWifi() {
  WiFi.begin(ssid, password);
  int status = WiFi.status();
  while (status == 6 || status == 4 || status == 5 || status == 1) {
    Serial.print(&quot;Failed to connect:&quot;); 
    Serial.print(status);
    Serial.print(&quot;\n&quot;);
    delay(1500);
    WiFi.begin(ssid, password);
  }
}

void send(String json) {
  HTTPClient http; 

  if(http.begin(webserver)){    

      int httpCode = http.GET();
      http.addHeader(&quot;Content-Type&quot;, &quot;application/json&quot;);
      http.addHeader(&quot;Host&quot;, &quot;something&quot;);
      // client.print(&quot;Content-Length: &quot;);
      delay(1500);  
      
      Serial.println(httpCode);
      Serial.println(http.getString());
      if (httpCode &gt; 0) {
      Serial.printf(&quot;[HTTP] GET... code: %d\n&quot;, httpCode);
        if(httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          String payload = http.getString();                 
          if (payload != &quot;false&quot;){
            Serial.println(payload); 
            delay(1500);  
          } else {
          Serial.println(&quot;Bad Playload&quot;);
          }
        } else {
          
        }
      } else {
      Serial.println(&quot;Error sending to HTTP&quot;);
      }
      http.end(); 
    } else {
      Serial.println(&quot;?&quot;);
    } 
}
</code></pre>
 89147: <p>I've figured out after an unreasonable amount of time that the request fails with a 400 as it doesn't have a slash at the end of the URL (<code>http://192.168.1.208:8090</code> -&gt; <code>http://192.168.1.208:8090/</code>). This must be something specific to NodeJS, as it works ok on a go server.</p>
 46852: <p>Your Arduino code reads like this:</p>
<pre><code>  int httpCode = http.GET();
  http.addHeader(&quot;Content-Type&quot;, &quot;application/json&quot;);
  http.addHeader(&quot;Host&quot;, &quot;something&quot;);
  // client.print(&quot;Content-Length: &quot;);
  delay(1500);  
  
  Serial.println(httpCode);
  Serial.println(http.getString());
  if (httpCode &gt; 0) {
</code></pre>
<p>The call <code>http.GET()</code> actually performs the HTTP request. You're adding headers <em>after</em> you perform the request. They need to be added before it. So your code should read:</p>
<pre><code>  // http.addHeader(&quot;Content-Type&quot;, &quot;application/json&quot;);
  int httpCode = http.GET();

  // delay(1500);   -- there's no need to call delay() here
  
  Serial.println(httpCode);
  Serial.println(http.getString());
  if (httpCode &gt; 0) {
</code></pre>
<p>HTTP status code 400 means &quot;bad request&quot;. Some web servers will return this code if headers that they expect to see are missing or have non-sensical values. The Go and Javascript web servers are different implementations and may behave differently with malformed requests.</p>
<p>I've removed &quot;Host&quot; header as <a href="https://github.com/espressif/arduino-esp32/blob/master/libraries/HTTPClient/src/HTTPClient.cpp#L1186" rel="nofollow noreferrer"><code>HTTPClient</code> does this for you</a>. And there's no need for the <code>delay()</code> call after calling <code>http.GET()</code>.</p>
<p><strong>Edit</strong>: I also commented out the &quot;Content-Type&quot; header as it makes no sense on a GET request, thanks to @EdgarBonet for mentioning that. I'm leaving it there as a comment to make it clear that that any headers should be added <em>before</em> the request is performed.</p>
