How to Get the Real Client IP Through Nginx/Apache Proxy?

When using nginx/apache as a proxy for Workerman, nginx/apache acts as a client to Workerman. Therefore, the client IP obtained on Workerman will be the IP of the nginx/apache server, not the actual client IP. To obtain the real client IP, you can refer to the methods below.

Principle:

Nginx/apache passes the real client IP through the HTTP header. For example, in the nginx configuration, add proxy_set_header X-Real-IP $remote_addr; in the location block. Workerman reads this header value and saves it in the $connection object (GatewayWorker can save it in the $_SESSION variable), which can be directly accessed when needed.

Note:

The following configuration is applicable to http/https and ws/wss protocols. For other protocols, the method of obtaining the client IP is similar, requiring the proxy server to insert a segment of IP data into the packet that transparently passes the real client IP.

Nginx configuration is as follows:

server {
  listen 443;

  ssl on;
  ssl_certificate /etc/ssl/server.pem;
  ssl_certificate_key /etc/ssl/server.key;
  ssl_session_timeout 5m;
  ssl_session_cache shared:SSL:50m;
  ssl_protocols SSLv3 SSLv2 TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;

  location /wss
  {
    proxy_pass http://127.0.0.1:8282;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    # This part uses http headers to transparently pass the real client IP
    proxy_set_header X-Real-IP $remote_addr;
  }

  # location / {} Other site configurations...
}

Workerman reads the client IP from the header set by nginx

<?php

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';

$worker = new Worker('websocket://0.0.0.0:7272');

/**
 * The callback onWebSocketConnect during the websocket handshake of the client
 * Obtain the X_REAL_IP value from the http header set by nginx in the onWebSocketConnect callback
 */
$worker->onWebSocketConnect = function(TcpConnection $connection, $request){
    /**
     * The connection object does not have a realIP attribute by default, here we dynamically add a realIP attribute to the connection object
     * Remember that PHP objects can have properties added dynamically, you can also use your preferred property name
     */
    //$connection->realIP = $_SERVER['HTTP_X_REAL_IP']; // Usage in workerman v4
    $connection->realIP = $request->header('x-real-ip'); // Usage in workerman v5
};
$worker->onMessage = function(TcpConnection $connection, $data)
{
    // When using the real client IP, directly use $connection->realIP
    $connection->send($connection->realIP);
};
Worker::runAll();

GatewayWorker gets the client IP from the header set by nginx

Add the following code to Events.php

class Events
{
   public static function onWebsocketConnect($client_id, $data)
   {    
        $_SESSION['realIP'] = $data['server']['HTTP_X_REAL_IP'];
   }
   // .... Other code omitted....
}

After adding the code, you need to restart GatewayWorker.

This way, you can obtain the real client IP in the onMessage and onClose methods in Events.php through $_SESSION['realIP'].

Note: $_SESSION['realIP'] cannot be used directly in onWorkerStart, onConnect, and onWorkerStop methods in Events.php.