diff --git a/ampel-firmware/web_config.h b/ampel-firmware/web_config.h
index 0595270a9337fb66b90364cfb266eb2eee5cd07b..03a56d338a4c59bc3fad717d055e5bf6ed4764f7 100644
--- a/ampel-firmware/web_config.h
+++ b/ampel-firmware/web_config.h
@@ -20,5 +20,11 @@ namespace web_config {
   void setWifiConnectionCallback(void (*function)());
   void setWifiConnectionFailedCallback(void (*function)());
   void update();
+
+#if defined(ESP8266)
+  extern ESP8266WebServer http;
+#elif defined(ESP32)
+  extern WebServer http;
+#endif
 }
 #endif
diff --git a/ampel-firmware/web_server.cpp b/ampel-firmware/web_server.cpp
index 56650e3f176c7e96c7013cf7c7b6689e29ec60df..2425b34a7ddd01e9665d5babc3e9277ac68dbde2 100644
--- a/ampel-firmware/web_server.cpp
+++ b/ampel-firmware/web_server.cpp
@@ -8,6 +8,7 @@
 #  include <ESPmDNS.h>
 #endif
 
+#include "web_config.h"
 #include "config.h"
 #include "util.h"
 #include "ntp.h"
@@ -56,135 +57,7 @@ namespace web_server {
   void handleWebServerCSV();
 #endif
 
-#if defined(ESP8266)
-  ESP8266WebServer http(80); // Create a webserver object that listens for HTTP request on port 80
-#elif defined(ESP32)
-  WebServer http(80);
-#endif
-
-  DNSServer dnsServer;
-
-  IotWebConf *iotWebConf;
-
-#define STRING_LEN 64
-
-// -- Configuration specific key. The value should be modified if config structure was changed.
-  const char config_version[] = "ampel_test_v3";
-
-  static const char chooserValues[][STRING_LEN] = { "red", "blue", "darkYellow" };
-  static const char chooserNames[][STRING_LEN] = { "Red", "Blue", "Dark yellow" };
-
-  iotwebconf::TextTParameter<STRING_LEN> stringParam = iotwebconf::Builder<iotwebconf::TextTParameter< STRING_LEN>>(
-      "stringParam").label("String param").build();
-  iotwebconf::ParameterGroup group1 = iotwebconf::ParameterGroup("group1", "");
-  iotwebconf::IntTParameter<int16_t> intParam =
-      iotwebconf::Builder<iotwebconf::IntTParameter<int16_t>>("intParam").label("Int param").defaultValue(30).min(1).max(
-          100).step(1).placeholder("1..100").build();
-// -- We can add a legend to the separator
-  iotwebconf::ParameterGroup group2 = iotwebconf::ParameterGroup("c_factor", "Calibration factor");
-  iotwebconf::FloatTParameter floatParam = iotwebconf::Builder<iotwebconf::FloatTParameter>("floatParam").label(
-      "Float param").defaultValue(0.0).step(0.1).placeholder("e.g. 23.4").build();
-  iotwebconf::CheckboxTParameter checkboxParam =
-      iotwebconf::Builder<iotwebconf::CheckboxTParameter>("checkParam").label("Check param").defaultValue(true).build();
-  iotwebconf::SelectTParameter<STRING_LEN> chooserParam =
-      iotwebconf::Builder<iotwebconf::SelectTParameter< STRING_LEN>>("chooseParam").label("Choose param").optionValues(
-          (const char*) chooserValues).optionNames((const char*) chooserNames).optionCount(
-          sizeof(chooserValues) / STRING_LEN).nameLength(STRING_LEN).build();
-
-  void update() {
-    iotWebConf->doLoop(); // Listen for HTTP requests from clients
-  }
-
-  void initialize() {
-    iotWebConf = new IotWebConf(ampel.sensorId, &dnsServer, &http, HTTP_PASSWORD, config_version);
-
-    const int ONBOARD_LED_PIN = 2;
-# ifdef ESP8266
-    iotWebConf->setStatusPin(ONBOARD_LED_PIN, LOW);
-# else
-    iotWebConf->setStatusPin(ONBOARD_LED_PIN, HIGH);
-# endif
-    iotWebConf->setWifiConnectionTimeoutMs(1000UL * WIFI_TIMEOUT);
-
-#if defined(ESP8266)
-    WiFi.hostname(ampel.sensorId);
-#elif defined(ESP32)
-    WiFi.setHostname(ampel.sensorId);
-#endif
-
-    group1.addItem(&intParam);
-    group2.addItem(&floatParam);
-    group2.addItem(&checkboxParam);
-    group2.addItem(&chooserParam);
-
-    iotWebConf->addSystemParameter(&stringParam);
-    iotWebConf->addParameterGroup(&group1);
-    iotWebConf->addParameterGroup(&group2);
-
-    iotWebConf->setWifiConnectionCallback([]() {
-      led_effects::showKITTWheel(color::green);
-      Serial.println();
-      Serial.print(F("WiFi - Connected! IP address: "));
-      IPAddress address = WiFi.localIP();
-      snprintf(wifi::local_ip, sizeof(wifi::local_ip), "%d.%d.%d.%d", address[0], address[1], address[2], address[3]);
-
-      ntp::initialize();
-
-      //FIXME: Somehow already started
-//      if (MDNS.begin(ampel.sensorId)) { // Start the mDNS responder for SENSOR_ID.local
-//        MDNS.addService("http", "tcp", 80);
-//        Serial.println(F("mDNS responder started"));
-//      } else {
-//        Serial.println(F("Error setting up MDNS responder!"));
-//      }
-
-#  ifdef AMPEL_MQTT
-      mqtt::initialize(ampel.sensorId);
-#  endif
-
-      Serial.println(wifi::local_ip);
-      Serial.print(F("You can access this sensor via http://"));
-      Serial.print(ampel.sensorId);
-      Serial.print(F(".local (might be unstable) or http://"));
-      Serial.println(WiFi.localIP());
-    });
-
-    iotWebConf->setWifiConnectionFailedHandler([]() -> iotwebconf::WifiAuthInfo* {
-      led_effects::showKITTWheel(color::red);
-      Serial.println(F("Connection to WiFi failed"));
-      return NULL;
-    });
-
-    iotWebConf->skipApStartup();
-    //TODO: Add callbacks
-    //TODO: Add LED effects
-    //TODO: Allow offline config loading
-    //TODO: Add default values for SSID/password
-    //TODO: Add other params
-    //TODO: Use HTTP_USER / HTTP_PASSWORD for config
-    //TODO: Move to own class
-    //TODO: Remove AP Password config?
-    //TODO: Save LoRaWAN key if possible?
-    //FIXME: Why does MQTT fail? (on ESP32)
-
-//    iotWebConf->loadConfig();
-    Serial.println("<<<<<<<<<<<<<<<");
-    Serial.println(stringParam.value());
-    Serial.println(intParam.value());
-    Serial.println(floatParam.value());
-    iotWebConf->init();
-    Serial.println(stringParam.value());
-    Serial.println(intParam.value());
-    Serial.println(floatParam.value());
-    Serial.println(">>>>>>>>>>>>>>>");
-
-    sensor_console::defineCommand("reset_config", []() {
-      Serial.println(F("Resetting config..."));
-      iotWebConf->getSystemParameterGroup()->applyDefaultValue();
-      iotWebConf->saveConfig();
-      Serial.println(F("Done!"));
-    }, F("(resets the complete IotWeb config)"));
-
+  void definePages() {
     header_template =
         PSTR("<!doctype html><html lang=en>"
             "<head>\n"
@@ -323,36 +196,27 @@ namespace web_server {
             "</html>");
 
     // Web-server
-    http.on("/", handleWebServerRoot);
-    http.on("/command", handleWebServerCommand);
+    web_config::http.on("/", handleWebServerRoot);
+    web_config::http.on("/command", handleWebServerCommand);
 #ifdef AMPEL_CSV
-    http.on(csv_writer::filename, handleWebServerCSV); //NOTE: csv_writer should have been initialized first.
-    http.on("/delete_csv", HTTP_POST, handleDeleteCSV);
+    web_config::http.on(csv_writer::filename, handleWebServerCSV); //NOTE: csv_writer should have been initialized first.
+    web_config::http.on("/delete_csv", HTTP_POST, handleDeleteCSV);
 #endif
-
-    http.on("/config", [] {
-      iotWebConf->handleConfig();
-    });
-    http.onNotFound([]() {
-      iotWebConf->handleNotFound();
-    });
-
-    //TODO: Only once wifi connected
   }
 
   // Allow access if http_user or http_password are empty, or if provided credentials match
   bool shouldBeAllowed() {
     return strcmp(config::http_user, "") == 0 || strcmp(config::http_password, "") == 0
-        || http.authenticate(config::http_user, config::http_password);
+        || web_config::http.authenticate(config::http_user, config::http_password);
   }
 
   void handleWebServerRoot() {
-    if (iotWebConf->handleCaptivePortal()) {
-      // -- Captive portal requests were already served.
-      return;
-    }
+//    if (web_config::handleCaptivePortal()) {
+//      // -- Captive portal requests were already served.
+//      return;
+//    }
     if (!shouldBeAllowed()) {
-      return http.requestAuthentication(DIGEST_AUTH);
+      return web_config::http.requestAuthentication(DIGEST_AUTH);
     }
 
     unsigned long ss = seconds();
@@ -375,8 +239,8 @@ namespace web_server {
 
     //    Serial.print(F("INFO - Header size : "));
     //    Serial.print(strlen(content));
-    http.setContentLength(CONTENT_LENGTH_UNKNOWN);
-    http.send_P(200, PSTR("text/html"), content);
+    web_config::http.setContentLength(CONTENT_LENGTH_UNKNOWN);
+    web_config::http.send_P(200, PSTR("text/html"), content);
 
     // Body
     snprintf_P(content, sizeof(content), body_template, ampel.sensorId, sensor::co2, sensor::temperature,
@@ -397,7 +261,7 @@ namespace web_server {
 
     //    Serial.print(F(" - Body size : "));
     //    Serial.print(strlen(content));
-    http.sendContent(content);
+    web_config::http.sendContent(content);
 
     // Script
     snprintf_P(content, sizeof(content), script_template
@@ -408,48 +272,48 @@ namespace web_server {
 
     //    Serial.print(F(" - Script size : "));
     //    Serial.println(strlen(content));
-    http.sendContent(content);
+    web_config::http.sendContent(content);
   }
 
 #ifdef AMPEL_CSV
   void handleWebServerCSV() {
     if (!shouldBeAllowed()) {
-      return http.requestAuthentication(DIGEST_AUTH);
+      return web_config::http.requestAuthentication(DIGEST_AUTH);
     }
     if (FS_LIB.exists(csv_writer::filename)) {
       fs::File csv_file = FS_LIB.open(csv_writer::filename, "r");
       char csv_size[10];
       snprintf(csv_size, sizeof(csv_size), "%d", csv_file.size());
-      http.sendHeader("Content-Length", csv_size);
-      http.streamFile(csv_file, F("text/csv"));
+      web_config::http.sendHeader("Content-Length", csv_size);
+      web_config::http.streamFile(csv_file, F("text/csv"));
       csv_file.close();
     } else {
-      http.send(204, F("text/html"), F("No data available."));
+      web_config::http.send(204, F("text/html"), F("No data available."));
     }
   }
 
   void handleDeleteCSV() {
     if (!shouldBeAllowed()) {
-      return http.requestAuthentication(DIGEST_AUTH);
+      return web_config::http.requestAuthentication(DIGEST_AUTH);
     }
     Serial.print(F("Removing CSV file..."));
     FS_LIB.remove(csv_writer::filename);
     Serial.println(F(" Done!"));
-    http.sendHeader("Location", "/");
-    http.send(303);
+    web_config::http.sendHeader("Location", "/");
+    web_config::http.send(303);
   }
 #endif
 
   void handleWebServerCommand() {
     if (!shouldBeAllowed()) {
-      return http.requestAuthentication(DIGEST_AUTH);
+      return web_config::http.requestAuthentication(DIGEST_AUTH);
     }
-    http.sendHeader("Location", "/");
-    http.send(303);
-    sensor_console::execute(http.arg("send").c_str());
+    web_config::http.sendHeader("Location", "/");
+    web_config::http.send(303);
+    sensor_console::execute(web_config::http.arg("send").c_str());
   }
 
   void handlePageNotFound() {
-    http.send(404, F("text/plain"), F("404: Not found"));
+    web_config::http.send(404, F("text/plain"), F("404: Not found"));
   }
 }
diff --git a/ampel-firmware/web_server.h b/ampel-firmware/web_server.h
index ad52b656bf25279d9816679e6de41a6046962985..df6df91377842e84532775eff8892cd90995e837 100644
--- a/ampel-firmware/web_server.h
+++ b/ampel-firmware/web_server.h
@@ -2,7 +2,6 @@
 #define WEB_SERVER_H_
 
 namespace web_server {
-  void initialize();
-  void update();
+  void definePages();
 }
 #endif