Struggling to get ESP8266 on Arduino Uno working reliably

I am struggling to get ESP8266 connected to an Arduino Uno working reliably as a webserver. The firmware of my ESP8266 is 1.1.1 - I don’t have the option (or knowledge) to update it at the moment. Below is my code. It works, barely, if I serve a small string. However, it usually closes the connection or just loads forever (crashes?) if I try to load the page from the browser three or four times. Ultimately I need to serve a webpage with json embedded in it that will load a second page served by the esp8266, a json file. I have a working demo of that but it crashes after a few retrievals. I understand that my html page is too long for strings so have been attempting to shift across to PROGMEM, initially testing with just a short string. I am storing and retrieving that correctly (I think, at least I can Serial.print it) but as soon as I try to write it to the ESP8266 I get a never ending load in my browser.

Where am I going wrong here? Is it the string/PROGMEM that is causing the issues or is there something else I’m missing in the AT commands (like some sort of ping to keep the connection open)?

//load softserial library
#include <SoftwareSerial.h>

#include <avr/pgmspace.h>

//set a boolean constant variable to true
//#define DEBUG true
const boolean DEBUG = true;

//RX (pin 2) goes to TX on esp8266, TX (pin 3) goes to RX on esp8266
SoftwareSerial esp8266(2, 3);

//input from the photoresistor
//int photoresistorpin = 0;

//create a PROGMEM variable
//String WEBPAGE = "hello";
static const char PROGMEM WEBPAGE[] = {"hello"};

static const char WEBPAGE[] PROGMEM = R"rawliteral(
<meta name="viewport" content="width=device-width, minimumscale=1, maximum-scale=1, initial-scale=1">
  <h1>Light:</h1><div id="light"></div>
function loadDoc()
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) 
      var obj = JSON.parse(this.responseText);
      document.getElementById('light').innerHTML =[0].datavalue;
  };'GET', 'data.json', true); xhttp.send();
var timedEvent = setInterval(function() { loadDoc(); }, 5000);
//const int WEBPAGE_len = sizeof(WEBPAGE)/sizeof(WEBPAGE[0]);


void setup()
  //open the serial port

  //print setup started in the serial monitor
  Serial.println("Setup started");

  //start esp8266 module (note: your esp's baud rate might be different)

  //reset esp8266 module
  senddata("AT+RST\r\n", 2000, DEBUG); 

  //set esp8266 as access point mode
  //1 = Station mode (client)
  //2 = Access point mode (host)
  //3 = Access point mode + Station mode
  senddata("AT+CWMODE=2\r\n", 1000, DEBUG); 

  //get ip address for esp8266
  senddata("AT+CIFSR\r\n", 2000, DEBUG);
  //configure esp8266 for multiple connections
  senddata("AT+CIPMUX=1\r\n", 1000, DEBUG);

  //turn on esp8266 server on port 80
  senddata("AT+CIPSERVER=1,80\r\n", 1000, DEBUG);

  //setup completed
  Serial.println("Setup done");

void loop()
  //take a reading from the photoresistor
  //int lightval = analogRead(photoresistorpin);
  int lightval = random(1000);
  //to test

  //is the esp8266 sending a message 
  if (esp8266.available())
    //if received data from esp8266
    if (esp8266.find("+IPD,"))
      //subtract 48 because the read() function returns the ASCII decimal 
      //value and 0 (the first decimal number) starts at 48
      int connectionid = - 48;

//Serial.print("string = ");

      //read the url sent by the client, look for the variable (/)
      String msg;
      msg = esp8266.readStringUntil(' ');
      String pathrequested = msg.substring(0);


        //create a senddata string to send the webpage to the esp8266
        String cipsend = "AT+CIPSEND=" + String(connectionid) + ",";
        //cipsend += WEBPAGE.length();
        cipsend += strlen_P(WEBPAGE);
        cipsend += "\r\n";
        char buffer[1000];
        strcpy_P(buffer, WEBPAGE);

        //senddata(cipsend, 500, DEBUG);
        //senddata(WEBPAGE, 500, DEBUG);
        senddata(buffer, 500, DEBUG);

        //create a string closecommand with the connection id and send it
        String closecommand = "AT+CIPCLOSE=" + String(connectionid) + "\r\n";
        senddata(closecommand, 500, DEBUG);

        //increment the count



void senddata(String command, const int timeout, boolean debug)
  //send the received command to the esp8266

  //set int variable to the number of millisends since Arduino began
  long int time = millis();

  //while the time and the timeout is less than the number of millisends since Arduino began
  while((time + timeout) > millis())
    //while the esp8266 is sending messages
      //display output in the serial window 


read suggested zoomkat's suggestion :


read suggested zoomkat's suggestion :

Jumping to another board is not a solution I'm afraid. This is for a class I'm teaching on Arduino - we need to stick with the combination of Uno and ESP8266 because a different board would simply be confusing and more cost for the students. If additional components are needed that's fine (such as the LD1117 regulator mentioned, which I'll try). Also, there are many many tutorials out there that use this combination and seem to get it working successfully so reluctant to just abandon the ESP8266.