Php – Proxy to keep connection to API alive

keepalivePHPPROXY

On one server I have some 30 PHP sites running under Apache. All those sites use the same (HTTP) API to fetch some data. The API is hosted elsewhere (under my control)

The API uses Nginx with keep-alive and the PHP sites use CURL for making the API requests.

A visitor to 1 of the 30 sites would generate an API call, and the connection to the API would be closed by apache/PHP as soon as the HTML is delivered to the visitor.

What I'm looking for is something like a local proxy to the API that is able to maintain the connection to it so the PHP sites can profit from the keepalive.

Anyway of accomplishing this?

Best Answer

Nginx configured as a reverse proxy can do this easily :

http {
 upstream remoteserver {
  # here you add your remote server's IPs or hostnames
  server 54.175.222.246; # for example here we use HTTPBin's address
  keepalive 10; # maintain a maximum of 10 open connections
 }

 server {
  listen 80;

  location / {
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # passing the client's IP to the remote server, on a local machine it doesn't do much though
   proxy_set_header Host $http_host; # passing the Host header as requested by the client, otherwise this will default to the pool's name, "remoteserver" in this case
   proxy_pass http://remoteserver; # sends the request off to the pool defined above
  }
 }
}

Now you can point your scripts to the local server instead of the remote one, here's a demo with curl :

$ curl http://localhost/get -H "Host: host header is passed normally"
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "host header is passed normally", 
    "User-Agent": "curl/7.29.0"
  }, 
  "origin": "127.0.0.1, 1.2.3.4", 
  "url": "http://host header is passed normally/get"
}

As you can see, even the Host header is passed as-is.

Or you can make the transition seamless by making the remote hostname point to your local machine, either in /etc/hosts or in your DNS resolver's configuration. In this case, make sure to use only IP addresses instead of hostnames in the pool definition in Nginx's config, otherwise the proxy will also loop back to itself and that would cause a bit of a disaster.

Once the hosts file has been changed accordingly, the proxying is seamless :

$ curl http://httpbin.org/get -v
* About to connect() to httpbin.org port 80 (#0)
*   Trying 127.0.0.1...
* Connected to httpbin.org (127.0.0.1) port 80 (#0)
> GET /get HTTP/1.1
> User-Agent: curl/7.29.0
> Host: httpbin.org
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: nginx/1.6.2
< Date: Sun, 15 Mar 2015 00:41:54 GMT
< Content-Type: application/json
< Content-Length: 198
< Connection: keep-alive
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Credentials: true
< 
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.29.0"
  }, 
  "origin": "127.0.0.1, 1.2.3.4", 
  "url": "http://httpbin.org/get"
}

As you can see, our local server behaves just like the remote one and any program attempting to access the remote hostname will actually connect to our local server.

Note that this may need additional configuration for HTTPS-based hosts.