简体   繁体   中英

PHP, How to Redirect/Forward HTTP Request with header and body?

I have a PHP page, main.php which is on server 1.

I have a PHP page main.php (same page, different code) on server 2.

main.php is a WebService.

I would like to forward the full HTTP request made to server 1, to server 2, so that when a user sends an HTTP request to main.php (server 1), it would get the response from main.php on server 2.

I would like the request made to server 2 to be exactly like the original request to server 1.

I take the Http Request data via:

$some_param  = $_REQUEST['param']
$body =file_get_contents('php://input');

and lets say i have

$server1_url = "11111";
$server2_url = "22222";

The motivation for this is, i have a production server and a staging server, i would like to direct some traffic to the new server to test the new functionality on the staging server.

How do i redirect the request with all the data or "cloning" the full request, sending it to the new server and returning the new response?

Thanks for your help!

ps i tried using php curl, but i dont understand how it works, also i found all kinds of answers, but none forward the Requests params and the body.

Again thanks!

If you have access to the Apache server config you can create a virtualhost with the following settings:

ProxyPreserveHost Off
ProxyPass / http://remotesite.domain.tld/
ProxyPassReverse / http://remotesite.domain.tld/
ProxyPassReverseCookieDomain remotesite.domain.tld proxysite.tld

You'll need to enable mod_proxy and mod_proxy_http for this. Substitute remotesite.domain.tld to the site you forward to, and proxysite.tld to the forwarder.

If you don't have access to the server config files, you can still do in php, by manually setting up curl and forward everything.

<?php

error_reporting(E_ALL);
ini_set('display_errors', '1');

/* Set it true for debugging. */
$logHeaders = FALSE;

/* Site to forward requests to.  */
$site = 'http://remotesite.domain.tld/';

/* Domains to use when rewriting some headers. */
$remoteDomain = 'remotesite.domain.tld';
$proxyDomain = 'proxysite.tld';

$request = $_SERVER['REQUEST_URI'];

$ch = curl_init();

/* If there was a POST request, then forward that as well.*/
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
    curl_setopt($ch, CURLOPT_POST, TRUE);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $_POST);
}
curl_setopt($ch, CURLOPT_URL, $site . $request);
curl_setopt($ch, CURLOPT_HEADER, TRUE);

$headers = getallheaders();

/* Translate some headers to make the remote party think we actually browsing that site. */
$extraHeaders = array();
if (isset($headers['Referer'])) 
{
    $extraHeaders[] = 'Referer: '. str_replace($proxyDomain, $remoteDomain, $headers['Referer']);
}
if (isset($headers['Origin'])) 
{
    $extraHeaders[] = 'Origin: '. str_replace($proxyDomain, $remoteDomain, $headers['Origin']);
}

/* Forward cookie as it came.  */
curl_setopt($ch, CURLOPT_HTTPHEADER, $extraHeaders);
if (isset($headers['Cookie']))
{
    curl_setopt($ch, CURLOPT_COOKIE, $headers['Cookie']);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

if ($logHeaders)
{
    $f = fopen("headers.txt", "a");
    curl_setopt($ch, CURLOPT_VERBOSE, TRUE);
    curl_setopt($ch, CURLOPT_STDERR, $f);
}

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);

$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $header_size);
$body = substr($response, $header_size);

$headerArray = explode(PHP_EOL, $headers);

/* Process response headers. */
foreach($headerArray as $header)
{
    $colonPos = strpos($header, ':');
    if ($colonPos !== FALSE) 
    {
        $headerName = substr($header, 0, $colonPos);
        
        /* Ignore content headers, let the webserver decide how to deal with the content. */
        if (trim($headerName) == 'Content-Encoding') continue;
        if (trim($headerName) == 'Content-Length') continue;
        if (trim($headerName) == 'Transfer-Encoding') continue;
        if (trim($headerName) == 'Location') continue;
        /* -- */
        /* Change cookie domain for the proxy */
        if (trim($headerName) == 'Set-Cookie')
        {
            $header = str_replace('domain='.$remoteDomain, 'domain='.$proxyDomain, $header);
        }
        /* -- */
        
    }
    header($header, FALSE);
}

echo $body;

if ($logHeaders)
{
    fclose($f);
}
curl_close($ch);

?>

EDIT:

And of course the script must be in the root directory of a (sub)domain. And you should have a .htaccess that rewrites everything to it:

RewriteEngine On
RewriteRule .* index.php

this is the solution i have found (there might be better)

 public static function getResponse ($url,$headers,$body)
    {
        $params = '?' . http_build_query($headers);

        $redirect_url = $url . $params;

        $ch = curl_init($redirect_url);

        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $response = curl_exec($ch);

        if (!isset($response))
            return null;
        return $response;
    }

I have used the code by Rehmat and Calmarius and made a few changes so now it handles multiple fields with same name like

<input type="text" name="example[]">
<input type="text" name="example[]">
<input type="text" name="example[]">

and to upload files too, including multiple files that use the same field name.

here is goes:

<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');

class proxy {
    private $logHeaders     = false;
    
    /* Site to forward requests to.  */
    private $site           = 'http://redirectToSite';
    /* Domains to use when rewriting some headers. */
    private $remoteDomain   = 'redirectToSite';
    private $proxyDomain    = 'yourproxydomain.com';

    public function __construct() {
        $request = $_SERVER['REQUEST_URI'];
        $ch = curl_init();

        /* If there was a POST request, then forward that as well.*/
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            $post   = $this->sanitizePostFields($_POST);
            $files  = $this->sanitizeFiles($_FILES);
            if ($files) {
                $post = array_merge($post, $files);
            }
            
            curl_setopt($ch, CURLOPT_POST, TRUE);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
            /*
            // this is enough if not uploading files
            curl_setopt(
                $ch,
                CURLOPT_POSTFIELDS,
                http_build_query($_POST)
            );
            */
        }
        
        curl_setopt($ch, CURLOPT_URL, $this->site . $request);
        curl_setopt($ch, CURLOPT_HEADER, TRUE);

        $headers = getallheaders();

        /*
            Translate some headers to make the remote party think we
            actually browsing that site.
        */
        $extraHeaders = array();
        if (isset($headers['Referer'])) {
            $extraHeaders[] = 'Referer: '. str_replace(
                $this->proxyDomain,
                $this->remoteDomain,
                $headers['Referer']
            );
        }
        if (isset($headers['Origin'])) {
            $extraHeaders[] = 'Origin: '. str_replace(
                $this->proxyDomain,
                $this->remoteDomain,
                $headers['Origin']
            );
        }

        /*
            Forward cookie as it came.
        */
        curl_setopt($ch, CURLOPT_HTTPHEADER, $extraHeaders);
        if (isset($headers['Cookie'])) {
            curl_setopt($ch, CURLOPT_COOKIE, $headers['Cookie']);
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        if ($this->logHeaders) {
            $f = fopen("headers.txt", "a");
            curl_setopt($ch, CURLOPT_VERBOSE, TRUE);
            curl_setopt($ch, CURLOPT_STDERR, $f);
        }

        //curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        $response = curl_exec($ch);

        $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $headers = substr($response, 0, $header_size);
        $body = substr($response, $header_size);

        $headerArray = explode(PHP_EOL, $headers);

        /* Process response headers. */
        foreach($headerArray as $header) {
            $colonPos = strpos($header, ':');
            if ($colonPos !== FALSE) {
                $headerName = substr($header, 0, $colonPos);

                /*
                    Ignore content headers, let the webserver decide how to
                    deal with the content.
                */
                if (trim($headerName) == 'Content-Encoding') continue;
                if (trim($headerName) == 'Content-Length') continue;
                if (trim($headerName) == 'Transfer-Encoding') continue;
                
                //if (trim($headerName) == 'Location') continue;
                
                /* -- */
                /* Change cookie domain for the proxy */
                if (trim($headerName) == 'Set-Cookie') {
                    $header = str_replace(
                        'domain='.$this->remoteDomain,
                        'domain='.$this->proxyDomain,
                        $header
                    );
                }
                /* -- */
                
                if (trim($headerName) == 'Location') {
                    $header = str_replace(
                        $this->remoteDomain,
                        $this->proxyDomain,
                        $header
                    );
                }
            }
            
            header($header, FALSE);
        }

        echo $body;

        if ($this->logHeaders) {
            fclose($f);
        }
        
        curl_close($ch);
    }

    private function sanitizePostFields($post, $fieldName = '') {
        if (empty($post)) { return false; }
        if (!is_array($post)) { return false; }
        
        $result = [];
        
        foreach ($post as $k => $v) {
            if (is_string($v)) {
                $result[($fieldName ? "{$fieldName}[{$k}]" : $k)] = $v;
            }
            elseif (is_array($v)) {
                $result = array_merge(
                    $result,
                    $this->sanitizePostFields($v, $k)
                );
            }
        }
        
        return $result;
    }

    private function sanitizeFiles($files) {
        if (empty($files)) { return false; }
        if (!is_array($files)) { return false; }
        
        $result = [];
        
        foreach ($files as $k => $v) {
            if (empty($v['name'])) { continue; }
            
            if (is_array($v['name'])) {
                // more than one file using the same name field[]
                $nFields = count($v['name']);
                for ($i = 0; $i < $nFields; $i++) {
                    if (empty($v['tmp_name'][$i])) { continue; }
                    $curl_file_upload = new CURLFile(
                        $v['tmp_name'][$i],
                        $v['type'][$i],
                        $v['name'][$i]
                    );
                    $result["{$k}[{$i}]"] = $curl_file_upload;
                }
            }
            else {
                $curl_file_upload = new CURLFile(
                    $v['tmp_name'],
                    $v['type'],
                    $v['name']
                );
                $result[$k] = $curl_file_upload;
            }
        }
        
        return $result;
    }
    
}

$proxy = new proxy();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM