Troubleshooting

Xdebug for debugging remote PHP code

Reviewed on 6 min read depuracion php

Xdebug lets you step through PHP code with breakpoints and examine a request's variables from a compatible IDE.

Use it preferably locally or on staging. A step-by-step session can expose to the IDE the values of variables, credentials, tokens and personal data processed by the request. If you exceptionally have to use it in production, limit the test to one authorised request, use a private trigger and disable the extension straight away when you have finished.

Xdebug can add noticeable overhead depending on the mode enabled, and it disables the PHP JIT while it is loaded. Check its real version; do not infer it only from the PHP version.

Check availability and enable Xdebug 3

Go to cPanel > Select PHP Version for the version that runs the path. This guide only applies if the selector offers the xdebug extension and, under Options, the xdebug.mode option. Enable the extension and set xdebug.mode to debug. This option is applied when PHP starts and cannot be set from .htaccess or .user.ini.

If any of these options is missing, do not assume Xdebug is available for that version. Do not try to install or load another extension inside the account.

If the domain's PHP handler supports Xdebug directives in .htaccess, temporarily add:

php_value xdebug.start_with_request trigger
php_flag xdebug.start_upon_error Off
php_flag xdebug.discover_client_host Off
php_value xdebug.client_host 127.0.0.1
php_value xdebug.client_port 9003
php_value xdebug.trigger_value VALOR_ALEATORIO_LARGO

Replace VALOR_ALEATORIO_LARGO with a private value. These options set start-on-trigger, disable automatic start on errors and prevent the destination being discovered from headers. Check their effective values: code that calls xdebug_break() can also start a connection. The private trigger controls the usual test, but it is not an authentication mechanism for the protocol. If the directives cause a 500 error, remove them and check the log: the code alone does not prove that the handler rejects them.

Create a reverse SSH tunnel

Xdebug opens the connection to the IDE; the IDE does not connect to Xdebug. In this setup, Xdebug connects to 127.0.0.1:9003 on the server and the SSH tunnel forwards that connection to 127.0.0.1:9003 on your computer, where the IDE is listening.

First enable SSH access and set up a key following the SSH access guide.

On Linux or macOS, run:

ssh -N -p 93 \
  -o ExitOnForwardFailure=yes \
  -R 127.0.0.1:9003:127.0.0.1:9003 \
  USUARIO_CPANEL@TU_DOMINIO.COM

-R opens port 9003 only on the server's loopback interface and forwards it to port 9003 on your computer. Keep the session open only during the test. If SSH cannot create the forward, it will exit with an error; do not expose the debugger port to the Internet instead.

On Windows, set up PuTTY with the domain and port 93, using the SSH key authorised in cPanel. In Connection | SSH | Tunnels, create a Remote forward from 127.0.0.1:9003 to the destination 127.0.0.1:9003. Do not enable options that allow listening on other server interfaces. If the session rejects the remote forward, this route is not available for the account.

Set up the IDE and reproduce the request

Open in the IDE the same version of the code that is deployed and set up the mapping between the remote path and the local project. Set the Xdebug port to 9003, enable listening for connections and add the breakpoints.

In PhpStorm, use Run > Start Listening for PHP Debug Connections. In NetBeans, set port 9003 and the remote path mapping. You do not need to sync the project over FTP to receive a debugging session.

Enable the trigger with a compatible browser extension set to the same private value, or add XDEBUG_TRIGGER=VALOR_ALEATORIO_LARGO only to the test request. Reproduce the required operation just once. Visiting the URL without the trigger should not start the session.

When you have finished, disable the trigger, close the SSH tunnel, remove the temporary directives, change xdebug.mode to off or disable the extension, and check that no Xdebug log with session data is left behind.

If you need to analyse performance rather than step through the code, use PHP X-Ray.

Tunnel limits and checks

Loopback avoids listening on a public interface, but it does not guarantee isolation between all the users of a shared server. Confirm with the provider that forwarding is allowed and isolated for your environment. If the port is in use, agree on another remote port and adjust xdebug.client_port; do not change another user's settings.

First check that a request without the trigger does not start the session, and then that the test request reaches the right breakpoint. Do not publish a URL with the trigger: it can end up in histories or logs. Also delete the debugging cookie when you have finished. See the Xdebug step debugging options if the session starts unexpectedly.

You may also find useful

Something wrong or out of date? Tell us and we will review it.