ESP32 WebServer Library –  hasResponseHeader()

Home / References / ESP32 Library / WebServer Library

Description

The hasResponseHeader() method is used to check if a specific response header exists in the current HTTP response before it is sent to the client. This method returns a boolean value indicating whether the specified header name has been added to the response using the sendHeader() method. It’s particularly useful for conditional header logic, debugging HTTP responses, implementing dynamic header management, and ensuring that required headers are properly set before sending responses. The method provides a reliable way to verify header presence without retrieving the header value, making it ideal for validation workflows and security checks where header existence is more important than header content.



Syntax and Usage

The hasResponseHeader() method has a simple syntax:

  • Check header existence: bool exists = server.hasResponseHeader("Header-Name") – Returns true if the specified response header exists, false otherwise.

For practical applications and examples of this method, please consult the “Example Code” section on this page. This section provides comprehensive guidance to help you better understand and apply the method effectively.



Arguments

  • name (String) – The name of the response header to check for existence. The comparison is case-insensitive, so “Content-Type”, “content-type”, and “CONTENT-TYPE” are all equivalent.


Return Value

The hasResponseHeader() method returns a bool value: true if the specified response header exists in the current response, or false if the header has not been set. This allows for clean conditional logic when managing response headers dynamically.



Example Code

Conditional Header Management System

This example demonstrates how to use the hasResponseHeader() method to implement a conditional header management system. The server checks for the existence of various response headers and adds missing ones based on different conditions, ensuring consistent and secure HTTP responses across all endpoints.

How to use this example: Upload this code to your ESP32 and replace the WiFi credentials. After connecting, access the main page at http://ESP32_IP/ to see the header validation dashboard. Test different endpoints to see conditional header logic in action: http://ESP32_IP/api/secure for security header validation, http://ESP32_IP/api/cached for caching header checks, and http://ESP32_IP/admin/status for admin-specific headers. The system will automatically add missing headers and report which headers were found or added.

For more ESP32 development resources and tutorials, visit the All About ESP32 Resources Hub on  AvantMaker.com

/*
 * Author: Avant Maker
 * Date: June 18, 2025
 * Version: 1.0
 * License: MIT 
 * 
 * Description: 
 * This example demonstrates how to use the hasResponseHeader() method to
 * implement a conditional header management system. The server checks for
 * the existence of various response headers and adds missing ones based on
 * different conditions, ensuring consistent and secure HTTP responses
 * across all endpoints.
 *
 * How to use this example:
 * Upload this code to your ESP32 and replace the WiFi credentials.
 * After connecting, access the main page at http://ESP32_IP/ to see the
 * header validation dashboard. Test different endpoints to see conditional
 * header logic in action: http://ESP32_IP/api/secure for security header
 * validation, http://ESP32_IP/api/cached for caching header checks, and
 * http://ESP32_IP/admin/status for admin-specific headers. The system will
 * automatically add missing headers and report which headers were found or added.
 *
 * Code Source:
 * This example code is sourced from the Comprehensive Guide
 * to the ESP32 Arduino Core Library, accessible on AvantMaker.com.
 * For additional code examples and in-depth documentation related to
 * the ESP32 Arduino Core Library, please visit:
 *
 * https://avantmaker.com/home/all-about-esp32-arduino-core-library/
 *
 * AvantMaker.com, your premier destination for all things
 * DIY, AI, IoT, Smart Home, and STEM projects. We are dedicated
 * to empowering makers, learners, and enthusiasts with
 * the resources they need to bring their nnovative ideas to life.
 */

#include <WiFi.h>
#include <WebServer.h>

const char* ssid = "your_wifi_ssid";
const char* password = "your_wifi_password";

WebServer server(80);

// Header validation statistics
struct HeaderValidationStats {
  unsigned long totalValidations = 0;
  unsigned long headersAdded = 0;
  unsigned long headersFound = 0;
  String lastValidatedEndpoint = "";
} stats;

// Function to ensure security headers are present
void ensureSecurityHeaders() {
  String endpoint = "Security Headers";
  stats.lastValidatedEndpoint = endpoint;
  stats.totalValidations++;
  
  Serial.println("=== Security Header Validation ===");
  
  // Check and add X-Content-Type-Options if missing
  if (!server.hasResponseHeader("X-Content-Type-Options")) {
    server.sendHeader("X-Content-Type-Options", "nosniff");
    stats.headersAdded++;
    Serial.println("✓ Added missing X-Content-Type-Options header");
  } else {
    stats.headersFound++;
    Serial.println("✓ X-Content-Type-Options header already exists");
  }
  
  // Check and add X-Frame-Options if missing
  if (!server.hasResponseHeader("X-Frame-Options")) {
    server.sendHeader("X-Frame-Options", "DENY");
    stats.headersAdded++;
    Serial.println("✓ Added missing X-Frame-Options header");
  } else {
    stats.headersFound++;
    Serial.println("✓ X-Frame-Options header already exists");
  }
  
  // Check and add X-XSS-Protection if missing
  if (!server.hasResponseHeader("X-XSS-Protection")) {
    server.sendHeader("X-XSS-Protection", "1; mode=block");
    stats.headersAdded++;
    Serial.println("✓ Added missing X-XSS-Protection header");
  } else {
    stats.headersFound++;
    Serial.println("✓ X-XSS-Protection header already exists");
  }
  
  // Check and add Strict-Transport-Security if missing
  if (!server.hasResponseHeader("Strict-Transport-Security")) {
    server.sendHeader("Strict-Transport-Security", "max-age=31536000");
    stats.headersAdded++;
    Serial.println("✓ Added missing Strict-Transport-Security header");
  } else {
    stats.headersFound++;
    Serial.println("✓ Strict-Transport-Security header already exists");
  }
}

// Function to ensure caching headers are properly set
void ensureCachingHeaders(bool shouldCache) {
  String endpoint = "Caching Headers";
  stats.lastValidatedEndpoint = endpoint;
  stats.totalValidations++;
  
  Serial.println("=== Caching Header Validation ===");
  
  if (shouldCache) {
    if (!server.hasResponseHeader("Cache-Control")) {
      server.sendHeader("Cache-Control", "public, max-age=3600");
      stats.headersAdded++;
      Serial.println("✓ Added Cache-Control header for caching");
    } else {
      stats.headersFound++;
      Serial.println("✓ Cache-Control header already exists");
    }
    
    if (!server.hasResponseHeader("ETag")) {
      server.sendHeader("ETag", "\"esp32-" + String(millis()) + "\"");
      stats.headersAdded++;
      Serial.println("✓ Added ETag header for caching");
    } else {
      stats.headersFound++;
      Serial.println("✓ ETag header already exists");
    }
  } else {
    if (!server.hasResponseHeader("Cache-Control")) {
      server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
      stats.headersAdded++;
      Serial.println("✓ Added Cache-Control header for no-caching");
    } else {
      stats.headersFound++;
      Serial.println("✓ Cache-Control header already exists");
    }
    
    if (!server.hasResponseHeader("Pragma")) {
      server.sendHeader("Pragma", "no-cache");
      stats.headersAdded++;
      Serial.println("✓ Added Pragma header for no-caching");
    } else {
      stats.headersFound++;
      Serial.println("✓ Pragma header already exists");
    }
  }
}

// Function to add application-specific headers
void ensureApplicationHeaders() {
  String endpoint = "Application Headers";
  stats.lastValidatedEndpoint = endpoint;
  stats.totalValidations++;
  
  Serial.println("=== Application Header Validation ===");
  
  if (!server.hasResponseHeader("X-Powered-By")) {
    server.sendHeader("X-Powered-By", "ESP32 Arduino Core");
    stats.headersAdded++;
    Serial.println("✓ Added X-Powered-By header");
  } else {
    stats.headersFound++;
    Serial.println("✓ X-Powered-By header already exists");
  }
  
  if (!server.hasResponseHeader("X-Server-Info")) {
    server.sendHeader("X-Server-Info", "ESP32 Conditional Header Manager");
    stats.headersAdded++;
    Serial.println("✓ Added X-Server-Info header");
  } else {
    stats.headersFound++;
    Serial.println("✓ X-Server-Info header already exists");
  }
}

void handleRoot() {
  // Add some initial headers
  server.sendHeader("X-Request-ID", String(millis()));
  server.sendHeader("X-Response-Time", String(micros()));
  
  // Use conditional header management
  ensureSecurityHeaders();
  ensureApplicationHeaders();
  ensureCachingHeaders(false); // Don't cache main page
  
  String html = "<!DOCTYPE html><html><head>";
  html += "<title>AvantMaker ESP32 Conditional Header Manager</title>";
  html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
  html += "<style>";
  html += "body{font-family:Arial,sans-serif;margin:20px;background:#f0f8ff;}";
  html += ".container{max-width:1000px;margin:0 auto;background:white;padding:30px;border-radius:12px;box-shadow:0 6px 20px rgba(0,0,0,0.1);}";
  html += ".header-status{background:#e8f5e8;padding:20px;border-radius:8px;margin:20px 0;border-left:5px solid #28a745;}";
  html += ".stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:15px;margin:25px 0;}";
  html += ".stat-card{background:linear-gradient(135deg,#4CAF50 0%,#45a049 100%);color:white;padding:25px;border-radius:10px;text-align:center;}";
  html += ".stat-number{font-size:2.5em;font-weight:bold;margin-bottom:8px;}";
  html += ".stat-label{font-size:0.95em;opacity:0.9;}";
  html += ".test-section{background:#fff3cd;padding:25px;border-radius:8px;margin:20px 0;border:1px solid #ffeaa7;}";
  html += ".btn{background:#007bff;color:white;padding:14px 22px;border:none;border-radius:6px;text-decoration:none;margin:8px;display:inline-block;transition:all 0.3s;}";
  html += ".btn:hover{background:#0056b3;transform:translateY(-2px);}";
  html += ".btn.success{background:#28a745;} .btn.success:hover{background:#1e7e34;}";
  html += ".btn.warning{background:#ffc107;color:#212529;} .btn.warning:hover{background:#e0a800;}";
  html += ".btn.info{background:#17a2b8;} .btn.info:hover{background:#138496;}";
  html += ".header-list{background:#f8f9fa;padding:20px;border-radius:8px;margin:15px 0;}";
  html += ".header-item{background:#e9ecef;margin:8px 0;padding:12px;border-radius:6px;font-family:monospace;}";
  html += "</style></head><body>";
  
  html += "<div class='container'>";
  html += "<h1>🛡️ AvantMaker ESP32 Conditional Header Manager</h1>";
  html += "<p>This system demonstrates the <code>hasResponseHeader()</code> method by implementing intelligent conditional header management.</p>";
  
  // Display current validation status
  html += "<div class='header-status'>";
  html += "<h3>✅ Header Validation Complete</h3>";
  html += "<p>All required headers were validated and missing headers were automatically added.</p>";
  html += "<p><strong>Total Response Headers:</strong> " + String(server.responseHeaders()) + "</p>";
  html += "<p><strong>Last Validation:</strong> " + stats.lastValidatedEndpoint + "</p>";
  html += "</div>";
  
  // Display statistics
  html += "<div class='stats-grid'>";
  html += "<div class='stat-card'><div class='stat-number'>" + String(stats.totalValidations) + "</div><div class='stat-label'>Total Validations</div></div>";
  html += "<div class='stat-card'><div class='stat-number'>" + String(stats.headersFound) + "</div><div class='stat-label'>Headers Found</div></div>";
  html += "<div class='stat-card'><div class='stat-number'>" + String(stats.headersAdded) + "</div><div class='stat-label'>Headers Added</div></div>";
  
  float successRate = (stats.totalValidations > 0) ? (float)(stats.headersFound + stats.headersAdded) / (stats.headersFound + stats.headersAdded + stats.totalValidations) * 100 : 100;
  html += "<div class='stat-card'><div class='stat-number'>" + String(successRate, 1) + "%</div><div class='stat-label'>Validation Success</div></div>";
  html += "</div>";
  
  // Show current headers
  html += "<div class='header-list'>";
  html += "<h3>📋 Current Response Headers</h3>";
  for (int i = 0; i < server.responseHeaders(); i++) {
    String headerName = server.responseHeaderName(i);
    String headerValue = server.responseHeader(i);
    html += "<div class='header-item'>" + headerName + ": " + headerValue + "</div>";
  }
  html += "</div>";
  
  // Test endpoints
  html += "<div class='test-section'>";
  html += "<h3>🧪 Test Conditional Header Logic</h3>";
  html += "<p>Click these endpoints to see how <code>hasResponseHeader()</code> enables intelligent header management:</p>";
  html += "<a href='/api/secure' class='btn success'>Security API (Auto-Security Headers)</a>";
  html += "<a href='/api/cached' class='btn info'>Cached API (Auto-Cache Headers)</a>";
  html += "<a href='/admin/status' class='btn warning'>Admin Status (Custom Headers)</a>";
  html += "<a href='/stats/reset' class='btn'>Reset Statistics</a>";
  html += "</div>";
  
  // Usage instructions
  html += "<h3>📖 How It Works</h3>";
  html += "<ul>";
  html += "<li>The system uses <code>hasResponseHeader()</code> to check if required headers exist</li>";
  html += "<li>Missing headers are automatically added with appropriate values</li>";
  html += "<li>Different endpoints have different header requirements</li>";
  html += "<li>All header validation activities are logged to Serial monitor</li>";
  html += "<li>Statistics track the effectiveness of the validation system</li>";
  html += "</ul>";
  
  html += "<h3>📊 System Information</h3>";
  html += "<p>Free Heap: " + String(ESP.getFreeHeap()) + " bytes | ";
  html += "Uptime: " + String(millis()/1000) + "s | ";
  html += "WiFi Signal: " + String(WiFi.RSSI()) + " dBm</p>";
  
  html += "</div></body></html>";
  
  server.send(200, "text/html", html);
  Serial.println("Main page served with " + String(server.responseHeaders()) + " headers");
}

void handleSecureAPI() {
  // Pre-add one security header
  server.sendHeader("X-Content-Type-Options", "nosniff");
  
  // Let the system add the rest conditionally
  ensureSecurityHeaders();
  
  String json = "{";
  json += "\"message\":\"Secure API with conditional security headers\",";
  json += "\"securityValidation\":\"completed\",";
  json += "\"totalHeaders\":" + String(server.responseHeaders()) + ",";
  json += "\"headersFound\":" + String(stats.headersFound) + ",";
  json += "\"headersAdded\":" + String(stats.headersAdded) + ",";
  json += "\"timestamp\":" + String(millis());
  json += "}";
  
  server.send(200, "application/json", json);
  Serial.println("Secure API served with conditional security headers");
}

void handleCachedAPI() {
  // Pre-add Cache-Control header
  server.sendHeader("Cache-Control", "public, max-age=1800");
  
  // Let the system handle other caching headers conditionally
  ensureCachingHeaders(true);
  ensureApplicationHeaders();
  
  String response = "Cached API Response\n\n";
  response += "Cache validation completed\n";
  response += "Headers found: " + String(stats.headersFound) + "\n";
  response += "Headers added: " + String(stats.headersAdded) + "\n";
  response += "Total headers: " + String(server.responseHeaders()) + "\n";
  
  server.send(200, "text/plain", response);
  Serial.println("Cached API served with conditional caching headers");
}

void handleAdminStatus() {
  // Add some admin-specific headers first
  server.sendHeader("X-Admin-Access", "authorized");
  server.sendHeader("X-Admin-Level", "full");
  
  // Then ensure standard headers
  ensureSecurityHeaders();
  ensureApplicationHeaders();
  
  // Check if specific admin headers exist
  bool hasAdminAccess = server.hasResponseHeader("X-Admin-Access");
  bool hasAdminLevel = server.hasResponseHeader("X-Admin-Level");
  bool hasServerInfo = server.hasResponseHeader("X-Server-Info");
  
  String status = "Admin Status Report\n\n";
  status += "=== Header Existence Check ===\n";
  status += "X-Admin-Access exists: " + String(hasAdminAccess ? "YES" : "NO") + "\n";
  status += "X-Admin-Level exists: " + String(hasAdminLevel ? "YES" : "NO") + "\n";
  status += "X-Server-Info exists: " + String(hasServerInfo ? "YES" : "NO") + "\n\n";
  status += "=== Statistics ===\n";
  status += "Total validations: " + String(stats.totalValidations) + "\n";
  status += "Headers found: " + String(stats.headersFound) + "\n";
  status += "Headers added: " + String(stats.headersAdded) + "\n";
  status += "Current response headers: " + String(server.responseHeaders()) + "\n";
  
  server.send(200, "text/plain", status);
  Serial.println("Admin status served with role-specific headers");
}

void handleResetStats() {
  stats.totalValidations = 0;
  stats.headersAdded = 0;
  stats.headersFound = 0;
  stats.lastValidatedEndpoint = "";
  
  server.send(200, "text/plain", "Header validation statistics reset!\n\nGo back to: http://" + WiFi.localIP().toString());
  Serial.println("Header validation statistics reset");
}

void setup() {
  Serial.begin(115200);
  Serial.println("Starting ESP32 Conditional Header Manager...");
  
  // Connect to WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  
  Serial.println("WiFi connected!");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  
  // Set up route handlers
  server.on("/", handleRoot);
  server.on("/api/secure", handleSecureAPI);
  server.on("/api/cached", handleCachedAPI);
  server.on("/admin/status", handleAdminStatus);
  server.on("/stats/reset", handleResetStats);
  
  // Start server
  server.begin();
  Serial.println("HTTP server started");
  Serial.println("Conditional header manager available at: http://" + WiFi.localIP().toString());
  Serial.println("Monitor Serial output for header validation logs");
}

void loop() {
  server.handleClient();
  
  // Optional: Print periodic statistics
  static unsigned long lastPrint = 0;
  if (millis() - lastPrint > 60000) { // Every 60 seconds
    lastPrint = millis();
    if (stats.totalValidations > 0) {
      Serial.println("📊 Header Validation Statistics:");
      Serial.println("  Total validations: " + String(stats.totalValidations));
      Serial.println("  Headers found: " + String(stats.headersFound));
      Serial.println("  Headers added: " + String(stats.headersAdded));
      Serial.println("  Success rate: " + String((float)(stats.headersFound + stats.headersAdded) / stats.totalValidations * 100, 1) + "%");
    }
  }
}
error: Content is protected !!