ESP8266WebServer cookie save problem

Trying to store a cookie with server.sendHeader("Set-Cookie", "Set-Cookie: cookiename=cookiecontent;expires=31536000;"); but it won't work, and ChatGPT can't help me further :frowning_face: :slight_smile: maybe I have more success here.

Here is the code

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
.
.
// Function to set a cookie
void setCookie(const char* cookieName, const char* cookieValue, int days) {
  String cookieHeader = "Set-Cookie: " + String(cookieName) + "=" + String(cookieValue);
  if (days > 0) {cookieHeader += ";expires=" + String(days * 24 * 60 * 60) + ";";     // Add expiration if days is greater than 0
  }
  server.sendHeader("Set-Cookie", cookieHeader);               // Send the cookie header to the client
  Serial.println("Set-Cookie " + cookieHeader);
}

You've got one too many "Set-Cookie" in there and you don't need to trailing semi-colon.

server.sendHeader("Set-Cookie", "cookiename=cookiecontent;expires=31536000");

Hi, the first "Set-Cookie" was a typo, in the code it is there only once. I removed the trailing semicolon, but it still don't save the cookie. I allow 3rd party cookies in the smartphone settings as well.

How can that be a typo?

Your string cookieHeader contains "Set-Cookie: ....." and then the .sendHeader() call uses the header "Set-Cookie" and the value is your cookieHeader String?

I removed the first "Set-Cookie" in the string as you can see in the code below:

// Function to set a cookie
void setCookie(const char* cookieName, const char* cookieValue, int days) {
  String cookieHeader = String(cookieName) + "=" + String(cookieValue);
  if (days > 0) {cookieHeader += ";expires=" + String(days * 24 * 60 * 60);     // Add expiration if days is greater than 0
  }
  server.sendHeader("Set-Cookie", cookieHeader);               // Send the cookie header to the client
  Serial.println("Set-Cookie " + cookieHeader);
}

And this is the print-screen result of the serial monitor:
image

but the getCookie returns a null and I cannot see any cookie on the smartphone either:

 // Function to get a cookie value by name
String getCookie(const char* cookieName) {
  String cookieValue = "";
  if (server.hasHeader("Cookie")) {                            // Check if the "Cookie" header is present in the request
    String cookies = server.header("Cookie");
    int start = cookies.indexOf(String(cookieName) + "=");     // Find the position of the cookie name in the header
    if (start != -1) {
      start += strlen(cookieName) + 1;                         // Move the start position to the beginning of the value
      int end = cookies.indexOf(';', start);                   // Find the position of the semicolon or the end of the header
      if (end == -1) {
        end = cookies.length();
      }
      cookieValue = cookies.substring(start, end);             // Extract the cookie value
      Serial.println("CookieValue " + cookieValue);
    }
  }
  return cookieValue;
}

As this problem is still not solved, I've simplified the code as much as possible to just read a cookie when connecting, showing it, and create a new one, which should show up at the next logon etc ...

This is the code used

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

const char* ssid = "ESP8266_SoftAP";
ESP8266WebServer server(80);

void handleRoot() {
  String storedCookieValue = "None";
  String newCookieValue = String(millis());

  if (server.hasHeader("Cookie")) {
    String cookie = server.header("Cookie");
    int pos = cookie.indexOf("clientID=");
    if (pos != -1) {
      pos += 9; // Length of "clientID="
      int endPos = cookie.indexOf(";", pos);
      if (endPos == -1) endPos = cookie.length();
      storedCookieValue = cookie.substring(pos, endPos);
    }
  }

  server.sendHeader("Set-Cookie", "clientID=" + newCookieValue + "; Path=/;");
  Serial.println("Stored Cookie: " + storedCookieValue);
  Serial.println("New Cookie: " + newCookieValue);

  String html = "<html>"
                "<head>"
                "<style>"
                "body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; font-family: Arial, sans-serif; background-color: #f0f0f0; }"
                "div { text-align: center; background: #fff; padding: 40px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }"
                "h1 { font-size: 3em; margin-bottom: 20px; }"
                "p { font-size: 1.5em; margin-bottom: 20px; }"
                "</style>"
                "</head>"
                "<body>"
                "<div>"
                "<h1>Stored Cookie Value: " + storedCookieValue + "</h1>"
                "<p>New Cookie Value: " + newCookieValue + "</p>"
                "</div>"
                "</body>"
                "</html>";
  server.send(200, "text/html", html);
}

void setup() {
  Serial.begin(115200);
  WiFi.softAP(ssid);

  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);

  server.on("/", handleRoot);
  server.onNotFound([]() {
    server.send(404, "text/plain", "Not found");
  });

  server.begin();
}

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

And this is the result on the serial monitor for the first logon (which is strange that it shows it 2 times)
17:07:11.008 -> Stored Cookie: None
17:07:11.008 -> New Cookie: 33970
17:07:29.009 -> Stored Cookie: None
17:07:29.009 -> New Cookie: 51951

And this for the second logon
17:08:35.230 -> Stored Cookie: None
17:08:35.230 -> New Cookie: 118209

I want to use SoftAP on the NODEMCU as a stand-alone device without internet access.

Any help to solve this will be greatly appreciated (ChatGPT doesn't manage to solve the issue :slight_smile

I don't have an ESP8266, but ESPAsyncWebServer works on that and ESP32, and the usage is similar, with handler functions. The function receives the request as an argument, and you call methods on that. With their "simple_server" example, you can replace the handler for "/" with your code with just a few modifications

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    String storedCookieValue = "None";
    String newCookieValue = String(millis());

    if (request->hasHeader("Cookie")) {
      String cookie = request->header("Cookie");
      int pos = cookie.indexOf("clientID=");
      if (pos != -1) {
        pos += 9;  // Length of "clientID="
        int endPos = cookie.indexOf(";", pos);
        if (endPos == -1) endPos = cookie.length();
        storedCookieValue = cookie.substring(pos, endPos);
      }
    }

    // no such method -- equivalent one at the end, below
    // request->sendHeader("Set-Cookie", "clientID=" + newCookieValue + "; Path=/;");
    Serial.println("Stored Cookie: " + storedCookieValue);
    Serial.println("New Cookie: " + newCookieValue);

    String html = "<html>"
                  "<head>"
                  "<style>"
                  "body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; font-family: Arial, sans-serif; background-color: #f0f0f0; }"
                  "div { text-align: center; background: #fff; padding: 40px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }"
                  "h1 { font-size: 3em; margin-bottom: 20px; }"
                  "p { font-size: 1.5em; margin-bottom: 20px; }"
                  "</style>"
                  "</head>"
                  "<body>"
                  "<div>"
                  "<h1>Stored Cookie Value: " + storedCookieValue + "</h1>"
                  "<p>New Cookie Value: " + newCookieValue + "</p>"
                  "</div>"
                  "</body>"
                  "</html>";
    auto resp = request->beginResponse(200, "text/html", html);
    resp->addHeader("Set-Cookie", "clientID=" + newCookieValue + "; Path=/;");
    request->send(resp);
  });

Note the last three statements. It works for me.

Back to ESP8266WebServer: your usage of sendHeader before send looks right, based on their other examples. You can use a Network inspector in a desktop browser to see if the Set-Cookie header is coming across.

You could also try some other header to in case it's a more general problem. For example, this is how it implements redirect

  void redirect(const String& url, const String& content = emptyString) {
    sendHeader(F("Location"), url, true);
    sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate"));
    sendHeader(F("Pragma"), F("no-cache"));
    sendHeader(F("Expires"), F("-1"));
    send(302, F("text/html"), content);  // send 302: "Found"
    if (content.isEmpty()) {
      // Empty content inhibits Content-length header so we have to close the socket ourselves.
      client().stop();  // Stop is needed because we sent no content length
    }
  }

so in your handler, you could have instead of send

  server.redirect("http://google.com/");

kenb4, super thank you ! this works effectively and I can now integrate the rest of my logic. This is the code I used:

#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>

// NODEMCU in stand-alone WiFi Access Point without internet access

const char* ssid = "ESP8266_SoftAP";
AsyncWebServer server(80);

void handleRoot(AsyncWebServerRequest *request) {
  String storedCookieValue = "None";
  String newCookieValue = String(millis());

  if (request->hasHeader("Cookie")) {
    String cookie = request->header("Cookie");
    int pos = cookie.indexOf("clientID=");
    if (pos != -1) {
      pos += 9;  // Length of "clientID="
      int endPos = cookie.indexOf(";", pos);
      if (endPos == -1) endPos = cookie.length();
      storedCookieValue = cookie.substring(pos, endPos);
    }
  }

  Serial.println("Stored Cookie: " + storedCookieValue);
  Serial.println("New Cookie: " + newCookieValue);

  String html = "<html><head><style>"
                "body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; font-family: Arial, sans-serif; background-color: #f0f0f0; }"
                "h1 { font-size: 3em; margin-bottom: 20px; }</style></head><body>"
                "<h1>Stored Cookie Value: " + storedCookieValue + "<br> New Cookie Value: " + newCookieValue + "</h1></body></html>";
  
  auto resp = request->beginResponse(200, "text/html", html);
  resp->addHeader("Set-Cookie", "clientID=" + newCookieValue + "; Path=/;");
  request->send(resp);
}

void setup() {
  Serial.begin(115200);
  WiFi.softAP(ssid);

  IPAddress IP = WiFi.softAPIP();
  Serial.println("");
  Serial.println("");
  Serial.print("AP IP address: ");
  Serial.println(IP);

  server.on("/", HTTP_GET, handleRoot);
  Serial.println("...server.on / HTTP_GET");
  server.onNotFound([](AsyncWebServerRequest *request) {
    request->send(404, "text/plain", "Not found");
    Serial.println("...server.onNotFound");
  });

  server.begin();
  Serial.println("...server.begin");
}

void loop() {
  // No need to call server.handleClient() for AsyncWebServer
}

and this is the result:
14:29:37.082 ->
14:29:37.082 -> AP IP address: 192.168.4.1
14:29:37.082 -> ...server.on / HTTP_GET
14:29:37.082 -> ...server.begin
14:30:02.161 -> Stored Cookie: None
14:30:02.161 -> New Cookie: 25280

14:30:06.473 -> Stored Cookie: None
14:30:06.553 -> New Cookie: 29609
14:30:06.600 -> ...server.onNotFound

14:30:26.937 -> Stored Cookie: 29609
14:30:27.013 -> New Cookie: 50073

14:30:47.472 -> Stored Cookie: 50073
14:30:47.550 -> New Cookie: 70608

with the following comment:
The first time the IP address is entered it shows 2 entries None+25280 and None+29609 and only the last pair is correct. It is also only 29609 that is saved as a cookie.
This is then retrieved with the next time the browser accesses the IP, it reads 29609 and saves 50073.
This repeats correctly at each new browser IP access: read 50073 and saves 70608.

If I could get rid of the very first one, then it will be 100% clean. If not possible, then I will put a connection counter and skip the 1st one.
Again, many thanks, you're better than ChatGPT :slight_smile:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.