skip to content

In Xdebug 3, how do xdebug.start_with_request=yes and =trigger differ, and how do XDEBUG_TRIGGER and XDEBUG_SESSION start a session?

level: middleimportance: must knowfreq 45%

answer

  1. default means trigger for debug
  2. GET, POST, cookie, then environment
  3. XDEBUG_SESSION is the legacy name
  4. XDEBUG_SESSION_START sets a cookie
  5. trigger_value as a shared secret

basics

~20 s

With start_with_request=yes, Xdebug tries to reach the IDE at the start of every request; with trigger, the default for debug mode, it connects only when an XDEBUG_TRIGGER or legacy XDEBUG_SESSION GET, POST, cookie or environment variable is present, or when xdebug_break() runs.

solid answer

~40 s

`xdebug.start_with_request=yes` makes every request connect to the IDE before any PHP code runs, convenient on a private machine but costly when the IDE is not listening, since each request waits up to `connect_timeout_ms` (200 ms). `trigger`, which `default` resolves to in debug mode, connects only when a trigger is present. Xdebug looks for `XDEBUG_TRIGGER` in GET, POST and cookies, then in the environment, and falls back to the legacy `XDEBUG_SESSION` name. Browser helper extensions set an `XDEBUG_SESSION` cookie; on the CLI you `export XDEBUG_SESSION=1` before running a script or tests. `?XDEBUG_SESSION_START=name` makes Xdebug set that cookie for later requests, with no expiry since 3.1, and `XDEBUG_SESSION_STOP` removes it. In trigger mode, `xdebug_break()` can also start a session. A non-empty `xdebug.trigger_value` makes only matching values work.

code

bash · 6 lines
bash
# web: one debugged request, then a cookie-managed session
curl 'http://localhost:8080/orders?XDEBUG_TRIGGER=1'
curl -c jar 'http://localhost:8080/?XDEBUG_SESSION_START=me'   # sets XDEBUG_SESSION cookie

# CLI: debug one test run
XDEBUG_SESSION=1 vendor/bin/phpunit --filter OrderTotalTest

go deeper

for a junior

Recall that debug mode waits for a trigger by default, and that a browser extension or XDEBUG_SESSION=1 on the CLI provides it.

for a middle

Explain where Xdebug looks for XDEBUG_TRIGGER, how the legacy XDEBUG_SESSION fallback and the START/STOP cookie work, and the cost of yes.

for a senior

Show you choose trigger plus trigger_value for shared environments and can debug workers with xdebug_connect_to_client() or xdebug_break().

for a principal

Standardise how a team triggers sessions across web, CLI and workers so debugging never leaks into shared or production traffic.

## The setting and its values `xdebug.start_with_request` controls whether a feature is activated **at the start of a request**. For step debugging, the relevant values are: | Value | What happens for step debugging | |---|---| | `yes` | every request tries to connect to the IDE before any PHP code runs | | `trigger` | only requests carrying a trigger connect | | `no` | step debugging never starts at request start | | `default` (the default) | resolves per mode; for `debug` it means **`trigger`** | So an untouched installation in debug mode waits for a trigger. ## yes: always connect `yes` is convenient on a laptop where the IDE is always listening, because every page load, CLI command and test run stops at breakpoints. The costs: - when the IDE is **not** listening, every request pays for a failed connection attempt, up to `xdebug.connect_timeout_ms` (200 ms by default), and the log fills with errors; - CLI tools that you did not mean to debug, such as Composer scripts or queue workers, also try to connect. ## trigger: connect on request With `trigger`, Xdebug checks for a trigger named **`XDEBUG_TRIGGER`**, looking in this order: 1. the `XDEBUG_TRIGGER` GET variable, POST variable or cookie; 2. the `XDEBUG_TRIGGER` environment variable. If none is found, it falls back to the **legacy, mode-specific name**, which for step debugging is `XDEBUG_SESSION`, in the same places. The legacy name is still what most tools send: - browser helper extensions set an `XDEBUG_SESSION` cookie while the debug toggle is on; - on the command line, `export XDEBUG_SESSION=1` before `php script.php` or `vendor/bin/phpunit` starts a session for that process; - setting the old `XDEBUG_CONFIG` environment variable to any value also activates the debugger, as a further legacy fallback. ## Cookie-managed sessions For a series of requests, Xdebug can manage the cookie itself: - `?XDEBUG_SESSION_START=anyname` on one request makes Xdebug set an `XDEBUG_SESSION` cookie, so later requests from that browser are debugged too, including assets and favicons that go through PHP; - since Xdebug 3.1 that cookie has **no expiry** (before 3.1 it lasted one hour); - `?XDEBUG_SESSION_STOP=1` removes it. To stop a particular sub-request from starting a session, such as a toolbar's background XHR, send `XDEBUG_IGNORE` with any value except `no` or `0`. ## Restricting who can trigger By default Xdebug does not care what value a trigger has. If `xdebug.trigger_value` is non-empty, only a matching value activates it; since 3.1 it accepts a comma-separated list. On a shared development server this acts as a **shared secret**, so a stray cookie cannot start sessions against someone's IDE. ## Other ways to start a session - **`xdebug_break()`**: when `start_with_request=trigger` and no session is active, calling it makes Xdebug try to connect and then break on that line. It returns `true` if a session is now active and the breakpoint was set, `false` otherwise. Inside an active session it just acts as a breakpoint. - **`xdebug.start_upon_error=yes`**: start a session when a notice or warning is emitted or a `Throwable` is thrown, regardless of `start_with_request`. - **`xdebug_connect_to_client()`** (3.1+): retry the connection mid-process, useful in a long-running worker that started before the IDE was listening. ## What happens when a trigger arrives but nobody listens A trigger only means Xdebug **tries**. If the IDE is not listening, Xdebug waits up to `connect_timeout_ms`, logs a failed attempt, and the request then runs normally without a debugger. Nothing breaks, but a forgotten `XDEBUG_SESSION` cookie adds that delay to every PHP request from the browser, including assets served through PHP, until the cookie is removed or the helper extension is switched off. `xdebug_is_debugger_active()` returns `true` only while a client is actually attached, which is how code can tell the two cases apart. ## Choosing - Personal machine, IDE always open: `yes` is fine. - Shared or containerised environments, or anything also running CLI tools: `trigger`, which is already the default. - Shared servers: `trigger` plus a `trigger_value`.

  • Why is start_with_request=yes a poor choice in a shared development container?
    Every request and every CLI command then tries to reach the IDE. When no one is listening, each one waits up to `connect_timeout_ms` (200 ms by default) and logs a failed connection, and a developer who is listening may get sessions from requests they never meant to debug. `trigger` confines debugging to requests that ask for it.
  • How do you debug a long-running queue worker that was started before the IDE began listening?
    Xdebug normally tries to connect only at the start of a request, and does not retry if no client was listening. In the worker, call `xdebug_connect_to_client()` (Xdebug 3.1+) at the start of a job to retry, or place `xdebug_break()` where you want to stop; in trigger mode that also attempts the connection.

saying these in an interview costs you the question

  • XDEBUG_SESSION no longer works in Xdebug 3
  • Debug mode connects on every request unless told otherwise
  • The trigger value must match the IDE key
  • xdebug_break() starts a session even with start_with_request=no
  • The XDEBUG_SESSION cookie expires after one hour in Xdebug 3.5