# vscode-php-debug: Xdebug breakpoints inside VS Code

> This extension is a debug adapter that sits between VS Code and Xdebug, so a PHP developer can set breakpoints, step through code and inspect variables in the editor instead of the terminal. It is MIT licensed and maintained in the Xdebug organisation, and it works by having Xdebug connect to a listener, which is why the port configuration and the Xdebug version matter more than usual.

**xdebug/vscode-php-debug** — PHP Debug Adapter for Visual Studio Code 🐞⛔

- Repository: https://github.com/xdebug/vscode-php-debug
- Stars: 835 · Forks: 180
- Language: TypeScript
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/xdebug-vscode-php-debug

## An adapter, not a debugger

The extension is a debug adapter between VS Code and Xdebug by Derick Rethans. That single sentence is the whole architecture, and it is worth being precise about the division of labour, because it explains every configuration decision you will meet.

Xdebug is a PHP extension, a .so file on Linux and a .dll on Windows, that has to be installed on your server. It is the thing that actually inspects the running program: it finds the breakpoints, reads the variables, controls the stepping. VS Code is the user interface, and this extension is the translator between the Debug Adapter Protocol that VS Code speaks and the Xdebug wire protocol that Xdebug speaks.

That means the extension alone does nothing. If Xdebug is not installed, or is not configured to talk to a debugger, the extension has nothing to talk to and the launch configuration will simply sit there. The README puts it plainly: for debugging to work, Xdebug must still be correctly installed.

The audience is PHP developers who use VS Code as their editor and want the debugging experience they would get from a dedicated IDE. The topics on the repository are debugger, php, vscode, vscode-extension and xdebug, and the marketplace listing is under the xdebug publisher with the extension id xdebug.php-debug. The extension is developed in the Xdebug GitHub organisation rather than by an individual, and the author is listed as Felix Becker with Damjan Cvetko as a contributor.

The scope of what it does is worth stating, because it is a debugger front end and nothing more. Breakpoints, stepping, exception handling, variable inspection, and the launch configurations to drive them. It is not a profiler, not a static analyser and not a replacement for Xdebug itself.

## Installing Xdebug, and the phpinfo trick

The extension install is trivial, from the command palette:

```bash
ext install php-debug
```

Press F1, type ext install php-debug. Everything else is Xdebug configuration, and the README is unusually helpful about it.

The recommended first step is to make a simple test.php file, put a phpinfo(); statement in there, copy the output and paste it into the Xdebug installation wizard, which will analyze it and give tailored installation instructions for your environment. The reason for that detour is that a PHP extension install depends on four variables at once, and getting one wrong produces a confusing failure: the PHP version, the architecture, whether the build is thread safe, and the Visual Studio compiler version on Windows.

For Windows the instruction is to download the appropriate precompiled DLL for your PHP version, architecture of 64 or 32 bit, thread safety of TS or NTS, and Visual Studio compiler version, and place it in the PHP extension folder. On Linux you either download the source as a tarball or clone it with git and compile it, or you check whether your distribution already offers prebuilt packages, which is the fast path when it works.

Then PHP has to be told to load it, by adding zend_extension=path/to/xdebug to your php.ini. The path of your php.ini is shown in your phpinfo() output under Loaded Configuration File, which is the other reason the phpinfo step comes first.

If you are doing web development, the README adds a line that saves an hour of confusion: do not forget to restart your webserver to reload the settings. And verification is done the same way, by checking your phpinfo() output for an Xdebug section.

## The Xdebug 3 versus Xdebug 2 settings difference

This is where most setup problems happen, because the configuration keys changed completely between major versions of Xdebug.

For Xdebug v3.x.x, the settings are:

the two keys for Xdebug 3 are the mode set to debug and start_with_request set to yes:

```ini
   xdebug.mode = debug
   xdebug.start_with_request = yes
```

For Xdebug v2.x.x, they are:

the Xdebug 2 form uses three keys:

```ini
   xdebug.remote_enable = 1
   xdebug.remote_autostart = 1
   xdebug.remote_port = 9000
```

The mechanism is the same in both, Xdebug connects out to a waiting debugger rather than the debugger connecting in, but every key name changed. If you paste the v2 block into an Xdebug 3 configuration, PHP will start with the extension loaded and the debugger will never connect, and nothing in the log will explain why.

The README recommends autostart, which is remote_autostart in Xdebug 2 and start_with_request in Xdebug 3, because it just works. The alternative approaches, cookies, query parameters or browser extensions, all require the request to carry a signal, and the autostart approach means every request attempts a connection, which is what a debugging session wants.

One detail that catches people out. The default Xdebug port changed between Xdebug v2 and v3, from 9000 to 9003. So an extension configured for 9003 and an Xdebug 2 installation listening on 9000 will not meet, and the error will look like a timeout rather than a configuration mismatch.

That is also why the extension ships separate launch configurations for Xdebug v2 installations, marked Legacy in the README, alongside the current ones.

## The three launch configurations and what they do

In your project, you open the debugger, click the little gear icon and choose PHP, and VS Code creates a launch configuration with three entries.

Listen for Xdebug simply starts listening on the specified port, by default 9003, for Xdebug to connect to. If you configured Xdebug the recommended way, every time you make a request with a browser to your webserver, or launch a CLI script, Xdebug will connect and you can stop on breakpoints and exceptions. This is the configuration you want for a real application served by a webserver.

Launch currently open script is an example of CLI debugging. It launches the currently opened script as a CLI, shows all stdout and stderr output in the debug console, and ends the debug session once the script exits.

Launch Built-in web server starts the PHP built-in web server on a random port and opens the browser with the serverReadyAction directive. The port is random, localhost:0, but can be changed to a desired fixed port such as localhost:8080. If a router script is needed you add it with the program directive, and additional PHP and Xdebug directives trigger debugging on every page load.

The built-in web server configuration is the underrated one. It lets you debug a PHP application with no Apache, no nginx and no virtual host, which is a much faster loop than configuring a real webserver, and the router script support means it works with front controller applications that would otherwise need rewrite rules.

There is also a path with no configuration at all. The README notes you can debug a script without launch.json. If no folder is open and the VS Code status bar is purple, pressing F5 starts the open script with Xdebug 3 specific parameters. If the php executable is not in path you can provide it with the setting php.debug.executablePath.

## The launch.json settings that matter in practice

The supported launch.json settings are listed in the README, and the ones that change real workflows are the filtering and mapping options.

The request setting is always "launch", which is a consequence of the architecture. Since Xdebug connects to the adapter rather than the other way around, VS Code cannot spawn the process, so there is no attach request type available.

pathMappings is a list of server paths mapping to the local source paths on your machine, and it is the setting that makes remote debugging work at all. When the code runs in a container, a VM or on a different host than your editor, the breakpoint has to be set against the path the server sees, and the mapping tells the adapter which local file to open. The hostname setting is the other half of remote work, because it is the address to bind to when listening for Xdebug, and it defaults to all IPv6 connections if available and otherwise all IPv4 connections, or it can be a Unix domain socket prefixed with unix:// or a Windows pipe in the form \\.?\pipe\name, and it cannot be combined with port.

The port setting defaults to 9003, and there is a useful detail here. If port is set to 0, a random port is chosen by the system and a placeholder ${port} is replaced with the chosen port in env and runtimeArgs. That is what makes the built-in web server configuration work on a random port without the adapter and the server disagreeing about it.

The three ignore options are about noise rather than behaviour. ignore is an array of glob patterns that errors should be ignored from, for example a pattern matching every PHP file under the vendor directory. ignoreExceptions is an array of exception class names that should be ignored, with examples like BaseException, a namespaced exception class, and wildcard forms matching any exception class. skipFiles is an array of glob patterns to skip when debugging, where star patterns and negations are allowed, for example a pattern matching the whole vendor tree, or a negated pattern that re-includes one module inside it. skipEntryPaths is an array of glob patterns to immediately detach from and ignore for debugging if the entry script matches, with a pattern such as an ajax entry script as the example.

That last one is for endpoints you do not want to step into at all, and the negation support in skipFiles means you can exclude a directory and then re-include one module inside it, which is the behaviour a Composer monorepo needs.

The log setting makes all communication between VS Code and the adapter go to the debug console, and the README points to the Troubleshooting section for it. When a launch configuration does not connect, that log is the first thing to turn on.

## Where this approach stops working

The fundamental limit is that Xdebug is a debugging extension inside the PHP process, and it changes how that process behaves. Every request through your webserver while Xdebug is loaded in debug mode pays for the debugger's presence. The extension does not change this, and it is the reason most teams keep the debugging configuration in a separate ini file that is not the one loaded in production.

The second limit is the connection model. Because Xdebug connects out to the adapter and the extension only supports the launch request type, there is no attach flow in the usual sense. That works well for a local script and for a webserver you control, and it works well for a container or a remote host as long as the network path and the path mappings are right. It is awkward in the case where you want to attach to a process that is already running, which is where a debugger with a proper attach mode is more comfortable.

The third is that the extension is a front end, so every limitation of Xdebug is a limitation here. The list of what the adapter can present is bounded by what Xdebug exposes, and the README does not claim otherwise.

The alternative approaches differ in what they cost. A full IDE such as PhpStorm ships with its own debugger integration and needs no separate extension or separate Xdebug configuration, at the cost of a licence and of leaving your editor. A different debug adapter, one targeting the same Xdebug protocol but maintained elsewhere, would behave the same in this respect because the Xdebug side is identical, so switching adapters buys you little. A newer PHP extension debugger is a different approach: it is a separate project with its own protocol and its own configuration, and it is designed to avoid some of the overhead that Xdebug's full debugging mode imposes, at the cost of leaving the Xdebug ecosystem that this extension and its documentation assume.

For a team that only needs to inspect a value in one place, a logging library or a var dump in the browser is a much cheaper answer than a debugging session. The extension earns its keep when you need to step through control flow in a complex legacy function, and it is worth remembering that the alternative to a debugging session is not nothing, it is a different tool.

## Maintenance, licence and what to check

The repository is not archived and the last push was on 2026-10-16, with releases v1.40.2 from 2026-09-18, v1.40.1 from 2026-04-07 and v1.40.0 from 2026-02-02. So the project is being worked on and shipping patch releases.

The versioning has a wrinkle worth knowing. The package.json carries the version 0.0.0-development, and the README carries a semantic-release badge, which means the published version number is derived from the commit messages rather than being written into the manifest. Conventional commit messages drive the release, and there is a commitlint configuration in the dev dependencies. In practice this means the marketplace version and the repository version track each other, but the manifest in the repository is always the placeholder.

The licence is MIT, with LICENSE.txt at the repository root, and the package.json also declares the license field as MIT. The extension is published on the Visual Studio Marketplace under the xdebug publisher, with a marketplace badge in the README, and the repository is the xdebug/vscode-php-debug project. The maintenance arrangement is worth naming: this is the debug adapter maintained in the same organisation as Xdebug itself, so the two sides are developed by the same people and the protocol compatibility is not a third party's problem.

What to check first, in order. Install Xdebug and confirm it appears in the phpinfo() output, since nothing else works until that is true. Then confirm which major version you have, because the settings keys and the default port differ between Xdebug 2 and Xdebug 3, and applying the wrong block is the most common cause of a silent failure to connect. Then check the port, 9003 by default, matches on both sides. Then, if the code runs somewhere other than your machine, set pathMappings before you start, because a missing mapping produces a breakpoint that never binds and an error that appears to be ignored.

The engines field asks for VS Code ^1.66.1 and Node ^20.18.2, so a recent editor and a recent Node are assumed for development, and the extension is built on the standard @vscode/debugadapter and @vscode/debugprotocol packages, which is the reason it is a small, well understood program rather than a large one.

## Conclusion

Adopt vscode-php-debug if you write PHP and want breakpoints, stepping and variable inspection in VS Code without paying for a full IDE, and you are willing to install and configure Xdebug on the machine running the code. Do not adopt it as a profiler or a production debugging strategy, because Xdebug in debug mode changes the runtime of the application it is attached to. Verify first that the Xdebug version on your server matches the settings you apply, that the listening port is 9003 for Xdebug 3 rather than 9000, and that your path mappings point at the local source when the code runs on a container or a remote host.

## FAQ

### How do I install vscode-php-debug and Xdebug?

Install the extension by pressing F1 and typing ext install php-debug, then install Xdebug itself as a PHP extension on the machine running the code, add zend_extension=path/to/xdebug to your php.ini, and enable remote debugging. The README recommends making a test.php file with a phpinfo(); statement and pasting the output into the Xdebug installation wizard.

### Which php.ini settings do I need for Xdebug 3?

Two keys, `xdebug.mode = debug` and `xdebug.start_with_request = yes`. The Xdebug 2 equivalents are `xdebug.remote_enable = 1`, `xdebug.remote_autostart = 1` and `xdebug.remote_port = 9000`, and the default port changed from 9000 to 9003 between the two versions.

### What is the difference between the launch configurations in vscode-php-debug?

Listen for Xdebug starts listening on the port, by default 9003, for Xdebug to connect. Launch currently open script runs the open script as a CLI and ends the session when it exits. Launch Built-in web server starts the PHP built-in web server on a random port, or a fixed one you choose, and opens the browser.

### How do I debug code that runs on a remote host or in a container?

Use the pathMappings launch.json setting, which lists server paths mapping to the local source paths on your machine. The hostname setting controls the address the adapter binds to when listening for Xdebug, and it can be a Unix domain socket with a unix:// prefix or a Windows pipe.

### How do I stop the debugger stepping into vendor code?

Use skipFiles with glob patterns that match the vendor tree, which supports negations so you can re-include one module inside it. There is also skipEntryPaths, which detaches from debugging entirely when the entry script matches a pattern, and ignore for errors and ignoreExceptions for exception classes.

### What license is vscode-php-debug released under?

MIT, with the LICENSE.txt file at the repository root and the license field in the package.json also set to MIT.

## Sources

- [Issues](https://github.com/xdebug/vscode-php-debug/issues)
- [License: MIT](https://github.com/xdebug/vscode-php-debug/blob/main/LICENSE)
- [README](https://github.com/xdebug/vscode-php-debug/blob/main/README.md)
- [Releases](https://github.com/xdebug/vscode-php-debug/releases)
- [xdebug/vscode-php-debug on GitHub](https://github.com/xdebug/vscode-php-debug)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/xdebug-vscode-php-debug
