├── .gitignore ├── LICENSE ├── Proxy.php ├── README.md ├── composer.json ├── composer.lock └── docker-compose.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | 4 | vendor/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /Proxy.php: -------------------------------------------------------------------------------- 1 | 11 | * @license http://unlicense.org 12 | * @package Zounar\PHPProxy 13 | * 14 | * Credits to: 15 | * https://github.com/cowboy/php-simple-proxy/ 16 | * https://gist.github.com/iovar 17 | * 18 | * Usage: 19 | * To call this script two headers must be sent 20 | * HTTP_PROXY_AUTH Access key for the proxy (should be changed) 21 | * HTTP_PROXY_TARGET_URL URL to be called by this script 22 | * 23 | * Debug: 24 | * To debug, send HTTP_PROXY_DEBUG header with any non-zero value 25 | * 26 | * Compatibility: 27 | * PHP >=5.6 28 | * libcurl 29 | * gzip 30 | * PHP safe_mode disabled 31 | */ 32 | class Proxy 33 | { 34 | /** 35 | * Your private auth key. It is recommended to change it. 36 | * If you installed the package via composer, call `Proxy::$AUTH_KEY = '';` before running the proxy. 37 | * If you copied this file, change the value here in place. 38 | * @var string 39 | */ 40 | public static $AUTH_KEY = 'Bj5pnZEX6DkcG6Nz6AjDUT1bvcGRVhRaXDuKDX9CjsEs2'; 41 | 42 | /** 43 | * Set this to false to disable authorization. Useful for debugging, not recommended in production. 44 | * @var bool 45 | */ 46 | public static $ENABLE_AUTH = true; 47 | 48 | /** 49 | * If true, PHP safe mode compatibility will not be checked 50 | * (you may not need it if no POST files are sent over proxy) 51 | * @var bool 52 | */ 53 | public static $IGNORE_SAFE_MODE = false; 54 | 55 | /** 56 | * Enable debug mode (you can do it by sending Proxy-Debug header as well). 57 | * This value overrides any value specified in Proxy-Debug header. 58 | * @var bool 59 | */ 60 | public static $DEBUG = false; 61 | 62 | /** 63 | * When set to false the fetched header is not included in the result 64 | * @var bool 65 | */ 66 | public static $CURLOPT_HEADER = true; 67 | 68 | /** 69 | * When set to false the fetched result is echoed immediately instead of waiting for the fetch to complete first 70 | * @var bool 71 | */ 72 | public static $CURLOPT_RETURNTRANSFER = true; 73 | 74 | /** 75 | * Target URL is set via Proxy-Target-URL header. For debugging purposes you might set it directly here. 76 | * This value overrides any value specified in Proxy-Target-URL header. 77 | * @var string 78 | */ 79 | public static $TARGET_URL = ''; 80 | 81 | /** 82 | * Name of remote debug header 83 | * @var string 84 | */ 85 | public static $HEADER_HTTP_PROXY_DEBUG = 'HTTP_PROXY_DEBUG'; 86 | 87 | /** 88 | * Name of the proxy auth key header 89 | * @var string 90 | */ 91 | public static $HEADER_HTTP_PROXY_AUTH = 'HTTP_PROXY_AUTH'; 92 | 93 | /** 94 | * Name of the target url header 95 | * @var string 96 | */ 97 | public static $HEADER_HTTP_PROXY_TARGET_URL = 'HTTP_PROXY_TARGET_URL'; 98 | 99 | /** 100 | * Line break for debug purposes 101 | * @var string 102 | */ 103 | protected static $HR = PHP_EOL . PHP_EOL . '----------------------------------------------' . PHP_EOL . PHP_EOL; 104 | 105 | /** 106 | * @return string[] 107 | */ 108 | protected static function getSkippedHeaders() 109 | { 110 | return [ 111 | static::$HEADER_HTTP_PROXY_TARGET_URL, 112 | static::$HEADER_HTTP_PROXY_AUTH, 113 | static::$HEADER_HTTP_PROXY_DEBUG, 114 | 'HTTP_HOST', 115 | 'HTTP_ACCEPT_ENCODING' 116 | ]; 117 | } 118 | 119 | /** 120 | * Return variable or default value if not set 121 | * @param mixed $variable 122 | * @param mixed|null $default 123 | * @return mixed 124 | * @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection 125 | */ 126 | protected static function ri(&$variable, $default = null) 127 | { 128 | if (isset($variable)) { 129 | return $variable; 130 | } else { 131 | return $default; 132 | } 133 | } 134 | 135 | /** 136 | * @param string $message 137 | */ 138 | protected static function exitWithError($message) 139 | { 140 | http_response_code(500); 141 | echo 'PROXY ERROR: ' . $message; 142 | exit(500); 143 | } 144 | 145 | /** 146 | * @return bool 147 | */ 148 | public static function isInstalledWithComposer() 149 | { 150 | $autoloaderPath = join(DIRECTORY_SEPARATOR, [dirname(dirname(__DIR__)), 'autoload.php']); 151 | return is_readable($autoloaderPath); 152 | } 153 | 154 | /** 155 | * @return void 156 | */ 157 | public static function registerErrorHandlers() 158 | { 159 | set_error_handler(function ($code, $message, $file, $line) { 160 | Proxy::exitWithError("($code) $message in $file at line $line"); 161 | }, E_ALL); 162 | 163 | set_exception_handler(function (Exception $ex) { 164 | Proxy::exitWithError("{$ex->getMessage()} in {$ex->getFile()} at line {$ex->getLine()}"); 165 | }); 166 | } 167 | 168 | /** 169 | * @return void 170 | */ 171 | public static function checkCompatibility() 172 | { 173 | if (!static::$IGNORE_SAFE_MODE && function_exists('ini_get') && ini_get('safe_mode')) { 174 | throw new RuntimeException('Safe mode is enabled, this may cause problems with uploading files'); 175 | } 176 | 177 | if (!function_exists('curl_init')) { 178 | throw new RuntimeException('libcurl is not installed on this server'); 179 | } 180 | 181 | if (!function_exists('gzdecode')) { 182 | throw new RuntimeException('gzip is not installed on this server'); 183 | } 184 | } 185 | 186 | /** 187 | * @return bool 188 | */ 189 | protected static function hasCURLFileSupport() 190 | { 191 | return class_exists('CURLFile'); 192 | } 193 | 194 | /** 195 | * @param string $headerString 196 | * @return string[] 197 | */ 198 | protected static function splitResponseHeaders($headerString) 199 | { 200 | $results = []; 201 | $headerLines = preg_split('/[\r\n]+/', $headerString); 202 | foreach ($headerLines as $headerLine) { 203 | if (empty($headerLine)) { 204 | continue; 205 | } 206 | 207 | // Header contains HTTP version specification and path 208 | if (strpos($headerLine, 'HTTP/') === 0) { 209 | // Reset the output array as there may by multiple response headers 210 | $results = []; 211 | continue; 212 | } 213 | 214 | $results[] = "$headerLine"; 215 | } 216 | 217 | return $results; 218 | } 219 | 220 | /** 221 | * Returns true if response code matches 2xx or 3xx 222 | * @param int $responseCode 223 | * @return bool 224 | */ 225 | public static function isResponseCodeOk($responseCode) 226 | { 227 | return preg_match('/^[23]\d\d$/', $responseCode) === 1; 228 | } 229 | 230 | /** 231 | * @return string 232 | */ 233 | protected static function getTargetUrl() 234 | { 235 | if (!empty(static::$TARGET_URL)) { 236 | $targetURL = static::$TARGET_URL; 237 | } else { 238 | $targetURL = static::ri($_SERVER[static::$HEADER_HTTP_PROXY_TARGET_URL]); 239 | } 240 | 241 | if (empty($targetURL)) { 242 | throw new RuntimeException(static::$HEADER_HTTP_PROXY_TARGET_URL . ' header is empty'); 243 | } 244 | 245 | if (filter_var($targetURL, FILTER_VALIDATE_URL) === false) { 246 | throw new RuntimeException(static::$HEADER_HTTP_PROXY_TARGET_URL . ' "' . $targetURL . '" is invalid'); 247 | } 248 | 249 | return $targetURL; 250 | } 251 | 252 | /** 253 | * @return bool 254 | */ 255 | protected static function isDebug() 256 | { 257 | return static::$DEBUG || !empty($_SERVER[static::$HEADER_HTTP_PROXY_DEBUG]); 258 | } 259 | 260 | /** 261 | * @return bool 262 | */ 263 | protected static function isAuthenticated() 264 | { 265 | return !static::$ENABLE_AUTH || static::ri($_SERVER[static::$HEADER_HTTP_PROXY_AUTH]) === static::$AUTH_KEY; 266 | } 267 | 268 | /** 269 | * @param string[] $skippedHeaders 270 | * @return string[] 271 | */ 272 | protected static function getIncomingRequestHeaders($skippedHeaders = []) 273 | { 274 | $results = []; 275 | foreach ($_SERVER as $key => $value) { 276 | if (in_array($key, $skippedHeaders)) { 277 | continue; 278 | } 279 | 280 | $loweredKey = strtolower($key); 281 | if (strpos($loweredKey, 'http_') === 0) { 282 | // Remove prefix 283 | $key = substr($loweredKey, strlen('http_')); 284 | // Replace underscores with dashes 285 | $key = str_replace('_', '-', $key); 286 | // Capital each word 287 | $key = ucwords($key, '-'); 288 | 289 | $results[] = "$key: $value"; 290 | } 291 | } 292 | 293 | return $results; 294 | } 295 | 296 | /** 297 | * @param string $targetURL 298 | * @return false|resource 299 | */ 300 | protected static function createRequest($targetURL) 301 | { 302 | $request = curl_init($targetURL); 303 | 304 | // Set input data 305 | $requestMethod = strtoupper(static::ri($_SERVER['REQUEST_METHOD'])); 306 | if ($requestMethod === "PUT" || $requestMethod === "PATCH") { 307 | curl_setopt($request, CURLOPT_POSTFIELDS, file_get_contents('php://input')); 308 | } elseif ($requestMethod === "POST") { 309 | $data = array(); 310 | 311 | if (!empty($_FILES)) { 312 | if (!static::hasCURLFileSupport()) { 313 | curl_setopt($request, CURLOPT_SAFE_UPLOAD, false); 314 | } 315 | 316 | foreach ($_FILES as $fileName => $file) { 317 | $filePath = realpath($file['tmp_name']); 318 | 319 | if (static::hasCURLFileSupport()) { 320 | $data[$fileName] = new CURLFile($filePath, $file['type'], $file['name']); 321 | } else { 322 | $data[$fileName] = '@' . $filePath; 323 | } 324 | } 325 | } 326 | 327 | curl_setopt($request, CURLOPT_POSTFIELDS, $data + $_POST); 328 | } 329 | 330 | $headers = static::getIncomingRequestHeaders(static::getSkippedHeaders()); 331 | 332 | curl_setopt_array($request, [ 333 | CURLOPT_FOLLOWLOCATION => true, 334 | CURLOPT_HEADER => static::$CURLOPT_HEADER, 335 | CURLOPT_RETURNTRANSFER => static::$CURLOPT_RETURNTRANSFER, 336 | CURLINFO_HEADER_OUT => true, 337 | CURLOPT_HTTPHEADER => $headers 338 | ]); 339 | 340 | return $request; 341 | } 342 | 343 | /** 344 | * @return int HTTP response code (200, 404, 500, etc.) 345 | */ 346 | public static function run() 347 | { 348 | if (!static::isAuthenticated()) { 349 | throw new RuntimeException(static::$HEADER_HTTP_PROXY_AUTH . ' header is invalid'); 350 | } 351 | 352 | $debug = static::isDebug(); 353 | $targetURL = static::getTargetUrl(); 354 | 355 | $request = static::createRequest($targetURL); 356 | 357 | // Get response 358 | $response = curl_exec($request); 359 | 360 | $headerSize = curl_getinfo($request, CURLINFO_HEADER_SIZE); 361 | $responseHeader = substr($response, 0, $headerSize); 362 | $responseBody = substr($response, $headerSize); 363 | $responseInfo = curl_getinfo($request); 364 | $responseCode = static::ri($responseInfo['http_code'], 500); 365 | $redirectCount = static::ri($responseInfo['redirect_count'], 0); 366 | $requestHeaders = preg_split('/[\r\n]+/', static::ri($responseInfo['request_header'], '')); 367 | if ($responseCode === 0) { 368 | $responseCode = 404; 369 | } 370 | 371 | $finalRequestURL = curl_getinfo($request, CURLINFO_EFFECTIVE_URL); 372 | if ($redirectCount > 0 && !empty($finalRequestURL)) { 373 | $finalRequestURLParts = parse_url($finalRequestURL); 374 | $effectiveURL = static::ri($finalRequestURLParts['scheme'], 'http') . '://' . 375 | static::ri($finalRequestURLParts['host']) . static::ri($finalRequestURLParts['path'], ''); 376 | } 377 | 378 | curl_close($request); 379 | 380 | //---------------------------------- 381 | 382 | // Split header text into an array. 383 | $responseHeaders = static::splitResponseHeaders($responseHeader); 384 | // Pass headers to output 385 | foreach ($responseHeaders as $header) { 386 | $headerParts = preg_split('/:\s+/', $header, 2); 387 | if (count($headerParts) !== 2) { 388 | throw new RuntimeException("Can not parse header \"$header\""); 389 | } 390 | 391 | $headerName = $headerParts[0]; 392 | $loweredHeaderName = strtolower($headerName); 393 | 394 | $headerValue = $headerParts[1]; 395 | $loweredHeaderValue = strtolower($headerValue); 396 | 397 | // Pass following headers to response 398 | if (in_array($loweredHeaderName, 399 | ['content-type', 'content-language', 'content-security', 'server'])) { 400 | header("$headerName: $headerValue"); 401 | } elseif (strpos($loweredHeaderName, 'x-') === 0) { 402 | header("$headerName: $headerValue"); 403 | } // Replace cookie domain and path 404 | elseif ($loweredHeaderName === 'set-cookie') { 405 | $newValue = preg_replace('/((?>domain)\s*=\s*)[^;\s]+/', '\1.' . $_SERVER['HTTP_HOST'], $headerValue); 406 | $newValue = preg_replace('/\s*;?\s*path\s*=\s*[^;\s]+/', '', $newValue); 407 | header("$headerName: $newValue", false); 408 | } // Decode response body if gzip encoding is used 409 | elseif ($loweredHeaderName === 'content-encoding' && $loweredHeaderValue === 'gzip') { 410 | $responseBody = gzdecode($responseBody); 411 | } 412 | } 413 | 414 | http_response_code($responseCode); 415 | 416 | //---------------------------------- 417 | 418 | if ($debug) { 419 | echo 'Headers sent to proxy' . PHP_EOL . PHP_EOL; 420 | echo implode(PHP_EOL, static::getIncomingRequestHeaders()); 421 | echo static::$HR; 422 | 423 | if (!empty($_GET)) { 424 | echo '$_GET sent to proxy' . PHP_EOL . PHP_EOL; 425 | print_r($_GET); 426 | echo static::$HR; 427 | } 428 | 429 | if (!empty($_POST)) { 430 | echo '$_POST sent to proxy' . PHP_EOL . PHP_EOL; 431 | print_r($_POST); 432 | echo static::$HR; 433 | } 434 | 435 | echo 'Headers sent to target' . PHP_EOL . PHP_EOL; 436 | echo implode(PHP_EOL, $requestHeaders); 437 | echo static::$HR; 438 | 439 | if (isset($effectiveURL) && $effectiveURL !== $targetURL) { 440 | echo "Request was redirected from \"$targetURL\" to \"$effectiveURL\""; 441 | echo static::$HR; 442 | } 443 | 444 | echo 'Headers received from target' . PHP_EOL . PHP_EOL; 445 | echo $responseHeader; 446 | echo static::$HR; 447 | 448 | echo 'Headers sent from proxy to client' . PHP_EOL . PHP_EOL; 449 | echo implode(PHP_EOL, headers_list()); 450 | echo static::$HR; 451 | 452 | echo 'Body sent from proxy to client' . PHP_EOL . PHP_EOL; 453 | } 454 | 455 | echo $responseBody; 456 | return $responseCode; 457 | } 458 | } 459 | 460 | if (!Proxy::isInstalledWithComposer()) { 461 | Proxy::checkCompatibility(); 462 | Proxy::registerErrorHandlers(); 463 | $responseCode = Proxy::run(); 464 | 465 | if (Proxy::isResponseCodeOk($responseCode)) { 466 | exit(0); 467 | } else { 468 | exit($responseCode); 469 | } 470 | } 471 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple PHP Proxy 2 | 3 | This proxy script allows you to forward all HTTP/HTTPS requests to another server. Works for all common request types 4 | including GET, POST requests with files, PATCH and PUT requests. It has minimal set of requirements 5 | (PHP >=5.6, libcurl, gzip) which are available even on the smallest free hostings and has its own simple authorization 6 | and cookie support. 7 | 8 | ## How to use 9 | * Copy the [Proxy.php](Proxy.php) script to publicly-accessible folder of a PHP web server (the script is standalone and has no PHP dependencies) 10 | * Make a cURL request targeting this script 11 | * Add **Proxy-Auth** header with auth key [found here](https://github.com/zounar/php-proxy/blob/master/Proxy.php#L40) 12 | * Add **Proxy-Target-URL** header with URL to be requested by the proxy 13 | * (Optional) Add **Proxy-Debug** header for debug mode 14 | 15 | In order to protect using proxy by unauthorized users, consider changing `Proxy-Auth` token in [proxy source file](https://github.com/zounar/php-proxy/blob/master/Proxy.php#L40) and in all your requests. 16 | 17 | ## How to use (via composer) 18 | This might be useful when you want to redirect requests coming into your app. 19 | 20 | * Run `composer require zounar/php-proxy` 21 | * Add `Proxy::run();` line to where you want to execute it (usually into a controller action) 22 | * In this example, the script is in `AppController` - `actionProxy`: 23 | ``` 24 | use Zounar\PHPProxy\Proxy; 25 | 26 | class AppController extends Controller { 27 | 28 | public function actionProxy() { 29 | Proxy::$AUTH_KEY = ''; 30 | // Do your custom logic before running proxy 31 | $responseCode = Proxy::run(); 32 | // Do your custom logic after running proxy 33 | // You can utilize HTTP response code returned from the run() method 34 | } 35 | } 36 | ``` 37 | * Make a cURL request to your web 38 | * In the example, it would be `http://your-web.com/app/proxy` 39 | * Add **Proxy-Auth** header with auth key [found here](https://github.com/zounar/php-proxy/blob/master/Proxy.php#L40) 40 | * Add **Proxy-Target-URL** header with URL to be requested by the proxy 41 | * (Optional) Add **Proxy-Debug** header for debug mode 42 | 43 | In order to protect using proxy by unauthorized users, consider changing `Proxy-Auth` token by calling 44 | `Proxy::$AUTH_KEY = '';` before `Proxy::run()`. Then change the token in all your requests. 45 | 46 | ## Usage example 47 | Following example shows how to execute GET request to https://www.github.com. Proxy script is at http://www.foo.bar/Proxy.php. All proxy settings are kept default, the response is automatically echoed. 48 | 49 | ```php 50 | $request = curl_init('http://www.foo.bar/Proxy.php'); 51 | 52 | curl_setopt($request, CURLOPT_HTTPHEADER, array( 53 | 'Proxy-Auth: Bj5pnZEX6DkcG6Nz6AjDUT1bvcGRVhRaXDuKDX9CjsEs2', 54 | 'Proxy-Target-URL: https://www.github.com' 55 | )); 56 | 57 | curl_exec($request); 58 | ``` 59 | 60 | ## Debugging 61 | In order to show some debug info from the proxy, add `Proxy-Debug: 1` header into the request. This will show debug info in plain-text containing request headers, response headers and response body. 62 | 63 | ```php 64 | $request = curl_init('http://www.foo.bar/Proxy.php'); 65 | 66 | curl_setopt($request, CURLOPT_HTTPHEADER, array( 67 | 'Proxy-Auth: Bj5pnZEX6DkcG6Nz6AjDUT1bvcGRVhRaXDuKDX9CjsEs2', 68 | 'Proxy-Target-URL: https://www.github.com', 69 | 'Proxy-Debug: 1' 70 | )); 71 | 72 | curl_exec($request); 73 | ``` 74 | 75 | ## Specifying User-Agent 76 | Some sites may return different content for different user agents. In such case add `User-Agent` header to cURL request, it will be automatically passed to the request for target site. In this case it's Firefox 70 for Ubuntu. 77 | 78 | ```php 79 | $request = curl_init('http://www.foo.bar/Proxy.php'); 80 | 81 | curl_setopt($request, CURLOPT_HTTPHEADER, array( 82 | 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:70.0) Gecko/20100101 Firefox/70.0', 83 | 'Proxy-Auth: Bj5pnZEX6DkcG6Nz6AjDUT1bvcGRVhRaXDuKDX9CjsEs2', 84 | 'Proxy-Target-URL: https://www.github.com' 85 | )); 86 | 87 | curl_exec($request); 88 | ``` 89 | 90 | ## Error 301 Moved permanently 91 | It might occur that there's a redirection when calling the proxy (not the target site), eg. during `http -> https` redirection. You can either modify/fix the proxy URL (which is recommended), or add `CURLOPT_FOLLOWLOCATION` option before `curl_exec`. 92 | 93 | ```php 94 | $request = curl_init('http://www.foo.bar/Proxy.php'); 95 | 96 | curl_setopt($request, CURLOPT_FOLLOWLOCATION, true ); 97 | curl_setopt($request, CURLOPT_HTTPHEADER, array( 98 | 'Proxy-Auth: Bj5pnZEX6DkcG6Nz6AjDUT1bvcGRVhRaXDuKDX9CjsEs2', 99 | 'Proxy-Target-URL: https://www.github.com' 100 | )); 101 | 102 | curl_exec($request); 103 | ``` 104 | 105 | ## Save response into variable 106 | The default cURL behavior is to echo the response of `curl_exec`. In order to save response into variable, all you have to do is to add `CURLOPT_RETURNTRANSFER` cURL option. 107 | 108 | ```php 109 | $request = curl_init('http://www.foo.bar/Proxy.php'); 110 | 111 | curl_setopt($request, CURLOPT_RETURNTRANSFER, true); 112 | curl_setopt($request, CURLOPT_HTTPHEADER, array( 113 | 'Proxy-Auth: Bj5pnZEX6DkcG6Nz6AjDUT1bvcGRVhRaXDuKDX9CjsEs2', 114 | 'Proxy-Target-URL: https://www.github.com' 115 | )); 116 | 117 | $response = curl_exec($request); 118 | ``` 119 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zounar/php-proxy", 3 | "description": "Forward your HTTP/HTTPS requests to another server.", 4 | "type": "library", 5 | "keywords": [ 6 | "php-proxy", 7 | "proxy-script", 8 | "php", 9 | "proxy", 10 | "http", 11 | "http-proxy", 12 | "curl", 13 | "curlphp" 14 | ], 15 | "homepage": "https://github.com/zounar/php-proxy", 16 | "readme": "README.md", 17 | "require": { 18 | "php": ">=5.6.0", 19 | "ext-curl": "*", 20 | "ext-zlib": "*" 21 | }, 22 | "license": "unlicense", 23 | "authors": [ 24 | { 25 | "name": "Robin Zounar", 26 | "homepage": "https://zounar.me" 27 | } 28 | ], 29 | "support": { 30 | "issues": "https://github.com/zounar/php-proxy/issues", 31 | "source": "https://github.com/zounar/php-proxy", 32 | "docs": "https://github.com/zounar/php-proxy/blob/master/README.md" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "Zounar\\PHPProxy\\": "./" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "a101b9e5b9b0bff50de607452cc30901", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "stable", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": { 16 | "php": ">=5.6.0", 17 | "ext-curl": "*", 18 | "ext-zlib": "*" 19 | }, 20 | "platform-dev": [], 21 | "plugin-api-version": "2.0.0" 22 | } 23 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | proxy: 4 | # image: webdevops/php-nginx:8.0 5 | # image: webdevops/php-nginx:7.4 6 | image: webdevops/php-nginx:5.6 7 | ports: 8 | - 80:80 9 | - 443:443 10 | volumes: 11 | - ./:/app 12 | --------------------------------------------------------------------------------