title: Varnish, Nginx, SSL - IP detection issue
body: <p>Hi I have a very strange issue and the hosting company is telling me its not possible I am hoping you guys can help.</p>
  
  <p>I have a nginx / varnish which acts as a load balancer and cache sitting in front of my webservers. My web servers are standard apache web servers.</p>
  
  <p>I am having problems with detecting the genuine IP address of users connecting to the system. The $_SERVER["REMOTE_ADDR"] shows the IP address of the load balancer/cache and not of the user visiting the site.</p>
  
  <p>We have written a hack so that with HTTP traffic we send through an X-Forwarded-For header with the correct IP address, but I have been told this is not possible with SSL.</p>
  
  <p>Unfortunately due to strict security requirements we are not able to offload SSL onto the load balancer/cache it has to go to apache.</p>
  
  <p>Has anyone got any ideas on how we could detect the IP address, when using SSL?</p>
  
  <p>thanks for the help, Si </p>
  
  <p>--- VCL Config ---</p>
  
  <pre><code>import std;
  
  C{
          #include &lt;stdlib.h&gt;
  }C
  
  
  
  sub vcl_recv {    
  /* Add X-Forwarded-For header */
      if (req.restarts == 0) {
          if (req.http.X-Forwarded-For) {
              set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
          } else {
              set req.http.X-Forwarded-For = client.ip;
          }
      }
  
  
  
      /* Fix compression */
      if (req.http.Accept-Encoding) {
          if (req.url ~ "\.(ico|png|jpe?g|gif|xpm|swf|flv|pdf|mp3|ogg|zip|gz|tgz|bz2|xz|7z)$") {
              remove req.http.Accept-Encoding;
          } else if (req.http.Accept-Encoding ~ "gzip") {
              set req.http.Accept-Encoding = "gzip";
          } else if (req.http.Accept-Encoding ~ "deflate") {
              set req.http.Accept-Encoding = "deflate";
          } else {
              remove req.http.Accept-Encoding;
          }
      }
  
      /* Handle SSL offloading */
      if (client.ip == "127.0.0.1") {
          std.log("SSL offloading detected " + client.ip + " " + req.http.X-Real-IP);
          set client.identity = req.http.X-Real-IP;
      } else {
          set client.identity = client.ip;
      }
  
      if (req.http.Cookie) {
         set client.identity = req.http.Cookie;
      }
  
  
      set req.http.X-Varnish-XID = req.xid;
      set req.backend = lb231;
  
      if (req.backend.healthy) {
          set req.grace = 30s;
      } else {
          set req.grace = 1h;
      }
  
          call normalise_user_agent;
  
      if (req.request == "PURGE") {
          if (!client.ip ~ purge) {
              error 405 "Not allowed.";
          }
          return(lookup);
      }
  
      if (req.request != "GET" &amp;&amp;
         req.request != "HEAD" &amp;&amp;
         req.request != "PUT" &amp;&amp;
         req.request != "POST" &amp;&amp;
         req.request != "TRACE" &amp;&amp;
         req.request != "OPTIONS" &amp;&amp;
         req.request != "DELETE") {
          return (pipe);
      }
  
      if (req.http.Cookie) {
                  set req.http.Cookie = ";" + req.http.Cookie;
                  set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
                  set req.http.Cookie = regsuball(req.http.Cookie, ";(PHPSESSID)=", "; \1="); # Cookies to keep here
                  set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
                  set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
  
                  if (req.http.Cookie == "") {
                          remove req.http.Cookie;
                  }
      }
  
      if (req.request != "GET" &amp;&amp; req.request != "HEAD") {
           return (pass);
      }
  
  
      if (req.url ~ "/nocache") {
          set req.http.X-No-Cache = "true";
          return (pass);
      }
  
      if (req.request == "BAN") {
          if (client.ip ~ purge) {
              error 401 "Forbidden";
          }
          ban("req.http.Host == " + req.http.X-VCL-Ban-Host + " &amp;&amp; req.url ~ " + req.http.X-VCL-Ban-URL);
          error 200 "Ban OK " + req.url + " " + req.http.Host;
      }
  
      if (req.request == "REFRESH") {
          set req.request = "GET";
          set req.hash_always_miss = true;
      }
  
      if (req.request != "GET" &amp;&amp; req.request != "HEAD") {
          return (pass);
      }
  
      return (lookup);
  }
  
  
      if (beresp.status &gt;= 400) {
          /* Cache error pages for a short amount of time */
          set beresp.ttl = 5s;
          set beresp.grace = 5s;
          set beresp.http.Cache-Control = "max-age=5, must-revalidate";
          unset beresp.http.Cookie;
          unset beresp.http.Set-Cookie;
      }
  
      if (beresp.status == 503) {
          /* Do not cache 503s at all */
          set beresp.ttl = 0s;
          set beresp.http.Cache-Control = "no-store, no-cache, must-revalidate";
          set beresp.http.Pragma = "no-cache";
      }
  
       if (beresp.http.X-Varnish-TTL) {
          C{
              char *ttl;
              ttl = VRT_GetHdr(sp, HDR_BERESP, "\016X-Varnish-TTL:");
              VRT_l_beresp_ttl(sp, atoi(ttl));
          }C
          #unset beresp.http.X-Varnish-TTL;
      } else {
          set beresp.ttl = 0s;
      }
  
      if (beresp.ttl &lt;= 0s) {
          set beresp.http.X-Cacheable = "No, not cacheable.";
      } elsif (req.http.Cookie ~ "(UserID|_session)") {
          set beresp.http.X-Cacheable = "No, got session.";
          return(hit_for_pass);
      } elsif (beresp.http.Cache-Control ~ "private") {
          set beresp.http.X-Cacheable = "No, Cache-Control=private";
          return(hit_for_pass);
      } else {
          set beresp.http.X-Cacheable = "Yes.";
          set beresp.grace = 1h;
      }
  }
  
  sub vcl_deliver {
      if (resp.http.reset-client-side-age) {
         unset resp.http.reset-client-side-age;
         set resp.http.Age = "0";
      }
  
      if (obj.hits &gt; 0) {
          set resp.http.X-Cache = "Hit";
          set resp.http.X-Cache-Hits = obj.hits;
      } else {
          set resp.http.X-Cache = "Miss";
      }
  
      if (resp.http.Server == "Varnish") {
          set resp.http.Server = "OnCommerce Framework Ltd";
      }
  
      unset resp.http.X-Varnish;
      unset resp.http.X-Scrubbed-For;
  }
  
  sub vcl_hit {
      if (req.request == "PURGE") {
          purge;
          error 200 "Purged.";
      }
  }
  
  sub vcl_miss {
      if (req.request == "PURGE") {
          purge;
          error 200 "Purged.";
      }
  }
  
  sub vcl_pipe {
      set bereq.http.Connection = "close";
  }
  
  sub vcl_error {
  
  }
  
  sub normalise_user_agent
  {
                  if(req.http.user-agent ~ "Mobile"){
                                  set req.http.X-UA = "mobile";
                  }
                  else if (req.http.user-agent ~ "Android")
                  {
                                  set req.http.X-UA = "android";
                  }
                  else if (req.http.user-agent ~ "Opera Mini/")
                  {
                                  set req.http.X-UA = "mobile";
                  }
                  else if (req.http.user-agent ~ "Opera Mobi/")
                  {
                                  set req.http.X-UA = "mobile";
                  }
                  else if (req.http.user-agent ~ "iP(ad|od|hone)/")
                  {
                                  set req.http.X-UA = "iOS";
                  }
                  else if (req.http.user-agent ~ "MSIE/")
                  {
                                  if(req.http.user-agent ~ "MSIE\s[1-7]/")
                                  {
                                                  set req.http.X-UA = "desktop-old";
                                  }
                                  else
                                  {
                                                  set req.http.X-UA = "desktop";
                                  }
                  }
                  else if (req.http.user-agent ~ "Chrome/")
                  {
                                  set req.http.X-UA = "desktop";
                  }
                  else if (req.http.user-agent ~ "Firefox/")
                  {
                                  set req.http.X-UA = "desktop";
                  }
                  else if (req.http.user-agent ~ "Waterfox/")
                  {
                                  set req.http.X-UA = "desktop";
                  }
                  else if (req.http.user-agent ~ "Safari/")
                  {
                                  set req.http.X-UA = "desktop";
                  }
                  else if (req.http.user-agent ~ "Opera/")
                  {
                                  set req.http.X-UA = "desktop";
                  }
                  else if (req.http.user-agent ~ "curl/")
                  {
                                  set req.http.X-UA = "desktop";
                  }
                  else
                  {
                                  set req.http.X-UA = req.http.user-agent;
                  }
  }
  </code></pre>