How to Accurately Detect Web Server (Apache/Nginx/IIS) in PHP Under All Configurations


2 views

When building WordPress plugins or any server-dependent PHP application, detecting the underlying web server is crucial for proper configuration. The challenge lies in the numerous possible server combinations and proxy setups that can obscure the actual server software.

The most straightforward approach examines the $_SERVER['SERVER_SOFTWARE'] variable:


function detectWebServer() {
    if (isset($_SERVER['SERVER_SOFTWARE'])) {
        $software = strtolower($_SERVER['SERVER_SOFTWARE']);
        
        if (strpos($software, 'apache') !== false) {
            return 'Apache';
        } elseif (strpos($software, 'nginx') !== false) {
            return 'Nginx';
        } elseif (strpos($software, 'microsoft-iis') !== false) {
            return 'IIS';
        } elseif (strpos($software, 'lighttpd') !== false) {
            return 'Lighttpd';
        }
    }
    return 'Unknown';
}

For more complex environments with proxies or load balancers, we need additional checks:


function advancedServerDetection() {
    // Check for common proxy headers
    $headers = array(
        'SERVER_SOFTWARE',
        'X-Powered-By',
        'SERVER_SIGNATURE',
        'X-Server'
    );
    
    $serverData = array();
    
    foreach ($headers as $header) {
        if (isset($_SERVER[$header])) {
            $serverData[] = $_SERVER[$header];
        }
    }
    
    // Check specific PHP SAPI
    if (php_sapi_name() === 'fpm-fcgi') {
        $serverData[] = 'PHP-FPM';
    }
    
    // Analyze collected data
    $combined = strtolower(implode('|', $serverData));
    
    if (strpos($combined, 'apache') !== false) {
        return 'Apache';
    } elseif (strpos($combined, 'nginx') !== false) {
        return 'Nginx';
    } elseif (strpos($combined, 'microsoft-iis') !== false) {
        return 'IIS';
    }
    
    // Fallback to SAPI detection
    if (strpos(php_sapi_name(), 'apache') !== false) {
        return 'Apache (module)';
    }
    
    return 'Unknown';
}

For reverse proxy setups where traffic passes through Nginx to Apache:


function detectWithProxy() {
    // Check X-Forwarded-Server header first
    if (isset($_SERVER['HTTP_X_FORWARDED_SERVER'])) {
        $forwarded = strtolower($_SERVER['HTTP_X_FORWARDED_SERVER']);
        if (strpos($forwarded, 'nginx') !== false) {
            return 'Nginx (reverse proxy)';
        }
    }
    
    // Fall back to regular detection
    return advancedServerDetection();
}

For WordPress plugins, we can enhance detection with WP-specific functions:


function wpDetectServer() {
    // Check for WP-CLI
    if (defined('WP_CLI') && WP_CLI) {
        return 'WP-CLI';
    }
    
    // Check for IIS with URL Rewrite
    if (isset($_SERVER['IIS_WasUrlRewritten']) && $_SERVER['IIS_WasUrlRewritten']) {
        return 'IIS with URL Rewrite';
    }
    
    // Standard detection
    $server = detectWithProxy();
    
    // Additional WP-specific checks
    if ($server === 'Unknown') {
        if (function_exists('is_nginx') && is_nginx()) {
            return 'Nginx (WP detected)';
        }
    }
    
    return $server;
}

When developing server-specific optimizations or configurations in PHP (like WordPress plugins), accurately detecting the underlying web server is crucial. The $_SERVER['SERVER_SOFTWARE'] superglobal is the primary source, but we need robust handling for various environments.


function detectWebServer() {
    $server_software = $_SERVER['SERVER_SOFTWARE'] ?? '';
    
    // Standard detection
    if (stripos($server_software, 'Apache') !== false) {
        return 'Apache';
    } elseif (stripos($server_software, 'nginx') !== false) {
        return 'Nginx';
    } elseif (stripos($server_software, 'Microsoft-IIS') !== false) {
        return 'IIS';
    } elseif (stripos($server_software, 'LiteSpeed') !== false) {
        return 'LiteSpeed';
    }
    
    // Additional checks when behind proxies
    $via = $_SERVER['HTTP_VIA'] ?? '';
    $forwarded = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
    
    if (!empty($via) || !empty($forwarded)) {
        // Check for common proxy headers
        if (isset($_SERVER['HTTP_X_SERVER_SOFTWARE'])) {
            $proxy_software = $_SERVER['HTTP_X_SERVER_SOFTWARE'];
            if (stripos($proxy_software, 'nginx') !== false) {
                return 'Nginx (behind proxy)';
            }
        }
    }
    
    // Fallback for PHP built-in server
    if (php_sapi_name() === 'cli-server') {
        return 'PHP Development Server';
    }
    
    return 'Unknown';
}

For complex setups like Apache+Nginx reverse proxy or PHP-FPM:


function advancedServerDetection() {
    // Check for FastCGI
    if (stripos(php_sapi_name(), 'fpm') !== false) {
        return 'PHP-FPM';
    }
    
    // Check for CGI
    if (strpos(php_sapi_name(), 'cgi') !== false) {
        return 'CGI Handler';
    }
    
    // Check server headers when behind load balancers
    $serverHeaders = [
        'SERVER_SOFTWARE',
        'X_Powered_By',
        'X-Server',
        'Server'
    ];
    
    foreach ($serverHeaders as $header) {
        if (isset($_SERVER[$header])) {
            $value = strtolower($_SERVER[$header]);
            if (strpos($value, 'nginx') !== false) return 'Nginx';
            if (strpos($value, 'apache') !== false) return 'Apache';
        }
    }
    
    return detectWebServer(); // Fallback to basic detection
}

Here's how to implement this in a WordPress plugin:


add_action('admin_init', 'check_server_environment');

function check_server_environment() {
    $server = advancedServerDetection();
    
    switch ($server) {
        case 'Apache':
            // Apply Apache-specific optimizations
            add_filter('mod_rewrite_rules', 'custom_apache_rules');
            break;
            
        case 'Nginx':
            // Apply Nginx-specific optimizations
            add_action('admin_notices', 'nginx_config_notice');
            break;
            
        case 'PHP-FPM':
            // Optimize for PHP-FPM
            ini_set('pm.max_children', 50);
            break;
            
        default:
            // Generic fallback
            break;
    }
}

Always verify your detection logic across multiple environments:


// Test cases
$testEnvironments = [
    'Apache' => 'Apache/2.4.41 (Ubuntu)',
    'Nginx' => 'nginx/1.18.0',
    'IIS' => 'Microsoft-IIS/10.0',
    'CloudFront' => 'Amazon CloudFront',
    'LiteSpeed' => 'LiteSpeed',
    'Unknown' => 'Caddy'
];

foreach ($testEnvironments as $expected => $input) {
    $_SERVER['SERVER_SOFTWARE'] = $input;
    $result = detectWebServer();
    echo "Expected: $expected, Got: $result" . PHP_EOL;
}