---
slug: "php-remote-debug"
title: "Remote Debugging via Reverse Port Forwarding with PHP SSH"
description: "When remote debugging PHP on a production server or another distant environment:"
url: "https://www.ytyng.com/en/blog/php-remote-debug"
publish_date: "2017-04-28T03:25:22Z"
created: "2017-04-28T03:25:22Z"
updated: "2026-02-27T05:09:35.568Z"
categories: ["PHP"]
keywords: ""
featured_image_url: "https://media.ytyng.com/resize/20230812/c5e41730419d4697b6e2a75ca275ddec.png.webp?width=768"
has_video: false
has_music: false
video_urls: []
music_urls: []
lang: "en"
---

# Remote Debugging via Reverse Port Forwarding with PHP SSH

<p>When remote debugging PHP on a production server or another distant environment:</p>
<p>Assume that the development PC is within an internal corporate LAN and cannot be accessed directly from the outside.</p>
<p>In this case, create a reverse SSH tunnel to enable communication between the production server and the local PC.</p>
<p></p>
<p>PHP xdebug configuration file on the production server</p>
<p>Example: /etc/php/5.6/apache2/conf.d/20-xdebug.ini</p>
<pre>[xdebug]<br />zend_extension=xdebug.so<br /><br />xdebug.remote_enable=1<br />xdebug.remote_port="9000"<br />xdebug.profiler_enable=0<br />xdebug.profiler_output_dir="/tmp"<br />xdebug.max_nesting_level=1000<br />xdebug.idekey = "PHPSTORM"</pre>
<p>When you run phpinfo(), you will see something like "Scan this dir for additional .ini files /etc/php/5.6/apache2/conf.d" which indicates where to find the configuration file.</p>
<p></p>
<p>With the above settings, during debugging (when the URL’s GET parameter includes something like ?XDEBUG_SESSION_START=12345), it will connect to port 9000 on the localhost.</p>
<p></p>
<p>On the development local PC, execute:</p>
<pre>$ ssh -R 127.0.0.1:9000:127.0.0.1:9000 user@example.com</pre>
<p>This will tunnel the production server's port 9000 to the local port 9000.</p>
<p></p>
<p>Then, when you run the debugger in PHPStorm, it will break at the breakpoints and allow you to debug.</p>
<p>Please ensure that you properly set up the server path mappings, otherwise it will not break properly.</p>
<p></p>
<p>Incidentally, since I use Python's Fabric for deployment, I create the tunnel with a command like this:</p>
<pre>@runs_once<br />def xdebug():<br />&nbsp;&nbsp;&nbsp; """<br />&nbsp;&nbsp;&nbsp; Creates an SSH tunnel for xdebug<br />&nbsp;&nbsp;&nbsp; """<br />&nbsp;&nbsp;&nbsp; local('ssh -R 127.0.0.1:9000:127.0.0.1:9000 {}@{}'.format(env.user, env.hosts[0]))</pre>
<p></p>
