Skip to content

Debug PHP-FPM

Debug a PHP application served by PHP-FPM — a request coming from a browser or curl stops on your breakpoint.

Prerequisites

  • php-dbgp-adapter on PATH
  • Xdebug installed in the PHP-FPM pool
  • PHP-FPM already running

Xdebug settings

In the ini used by the FPM pool:

xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=localhost
xdebug.client_port=9003

Restart PHP-FPM after editing:

sudo systemctl restart php-fpm      # or: brew services restart php

Verify Xdebug is actually loaded in the pool — not just in the CLI PHP, which is a different ini:

<?php phpinfo();

Look for an Xdebug section with mode => debug.

Config

version = 1
default = "web"

[profiles.web]
adapter = "php"
program = "."

[profiles.web.launch_arguments]
sessionMode = "server"
port = 9003

server is the mode for something already running: the adapter waits, and PHP requests connect to it as they arrive.

Run

  1. Set a breakpoint in a file the request will reach — a controller, not a bootstrap file that ran long ago.

  2. Start the session:

bugsaur gui --project . --break src/Controller/OrderController.php:45
  1. Send a request:
curl 'http://localhost:8080/orders?id=1'

What should happen

The request pauses, the debugger stops at line 45, and curl sits waiting for a response until you continue.

Requests sent before you started the session run normally, without debugging. That is the useful property of server mode: the site keeps working, and you debug only when you choose to.

Requests time out while you are stopped

A stopped request is a request the web server is still waiting on. Nginx or Apache will eventually give up:

fastcgi_read_timeout 3600s;

Raise it while debugging, or accept that long inspections end in a 504 on the client side — the debug session itself is unaffected.

Only debugging your own requests

On a shared or busy environment, start_with_request=yes makes every request try to connect. Use the trigger instead:

xdebug.start_with_request=trigger

Then only requests carrying the trigger are debugged:

curl -H 'Cookie: XDEBUG_TRIGGER=1' 'http://localhost:8080/orders?id=1'

Browser extensions for Xdebug set that cookie for you.

When it does not work

Symptom Fix
Nothing stops the request went out before the session started — send another
Nothing ever connects Xdebug is not loaded in the FPM ini; check phpinfo()
Breakpoints stay pending normal before the first request; after one, check paths
The client gets a 504 the web server's read timeout — raise it
Stops in a file you did not expect an earlier breakpoint bound in a shared bootstrap file