Informática y Software Práctico

XAMPP depurar PHP con Xdebug: por qué no se detiene

Post actualizado el día octubre 29, 2026 by DeiviSanzPlay

Si estás intentando XAMPP depurar PHP con Xdebug y el depurador nunca se detiene en el punto de ruptura, el problema casi nunca es Xdebug en sí. Casi siempre es la combinación de tres cosas: la versión de PHP que carga XAMPP, el php.ini que estás editando (que no es el que se lee) y el puerto que espera tu IDE. El síntoma típico: añades xdebug_break(), recargas la página y no pasa nada.

Antes de tocar nada, abre http://localhost/dashboard/phpinfo.php y busca la sección de Xdebug. Si pone xdebug.mode = develop y no debug, ya tienes la causa. En Xdebug 3 el modo por defecto es develop, y sin debug activo el depurador no intenta conectar con el cliente. Esto desconcierta porque en Xdebug 2 bastaba con activar la extensión.

El php.ini correcto en XAMPP: Apache no siempre lee el que crees

XAMPP tiene varios php.ini en el árbol de instalación. El panel de control edita uno, Apache puede usar otro y la CLI de PHP usa uno distinto. Para depurar peticiones web te interesa el que carga Apache, y lo confirmas mirando Loaded Configuration File en phpinfo. Si ahí aparece una ruta como C:xamppphpphp.ini, ese es el archivo, aunque hayas editado el de apachebin.

Un bloque mínimo que funciona para XAMPP depurar PHP con Xdebug en local es este:

zend_extension=xdebug
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=127.0.0.1
xdebug.client_port=9003
xdebug.log=C:xamppapachelogsxdebug.log

El puerto 9003 es el estándar de Xdebug 3; si tu IDE sigue esperando en 9000 (común en configuraciones antiguas de PHPStorm o VS Code), la conexión se rechaza sin mensaje visible en el navegador. Revisa el log de Xdebug antes de cambiar nada más: ahí aparece si intentó conectar y a qué dirección.

Por qué el punto de ruptura no se detiene aunque la conexión exista

Hay un caso que confunde mucho: Xdebug conecta correctamente, el IDE muestra el evento, pero el breakpoint se ignora. Casi siempre es porque el archivo abierto en el IDE no coincide con la ruta real que ejecuta el servidor. En Windows ocurre con frecuencia al trabajar con rutas que mezclan C:xampphtdocsproyecto y symlinks o unidades mapeadas. Configura el path mapping de tu IDE para apuntar a la ruta física que ve Apache.

También pasa con opcache. Si tienes opcache.enable=1 y opcache.validate_timestamps=0, PHP sirve el archivo en caché y tus cambios no se reflejan, así que el breakpoint queda en una línea que ya no existe en el archivo real. En desarrollo local, desactiva opcache o pon validate_timestamps=1. No es un fallo de XAMPP depurar PHP con Xdebug, es caché de bytecode.

Un ejemplo real de depuración que se atascó

Hace unos meses, un formulario de contacto en un proyecto sobre XAMPP dejaba de ejecutar el bloque tras un if sin lanzar error. Activé Xdebug con start_with_request=yes, puse un breakpoint en la línea del mail() y no se detenía. El log de Xdebug mostraba conexiones entrantes correctas. El problema era un return temprano que yo había añadido dos días antes y que no veía porque el archivo abierto en el editor era una copia en otra carpeta. Al abrir la ruta real, el breakpoint funcionó al primer intento. Moraleja: el log de Xdebug no miente; tu editor sí puede.

Xdebug con la CLI de PHP: aquí no funciona igual

Un escenario donde XAMPP depurar PHP con Xdebug no se comporta como esperas es la línea de comandos. Los scripts que ejecutas con php script.php usan el php.ini de la CLI, que en XAMPP suele ser otro archivo. Si depuras desde el navegador pero no desde la terminal, comprueba con php –ini qué configuración se carga. Además, en CLI necesitas xdebug.start_with_request=trigger o lanzar el script con la variable de entorno XDEBUG_SESSION=1; de lo contrario no hay petición HTTP que dispare la sesión.

Tampoco funciona bien en peticiones AJAX con start_with_request=yes si tu IDE solo escucha una sesión simultánea: dos peticiones paralelas del navegador pueden bloquear el depurador. Para esos casos, el modo trigger con cookie o parámetro es más estable.

Qué revisar en orden cuando no arranca

Primero, phpinfo: confirma la versión de Xdebug y el modo. Segundo, el log de Xdebug: si está vacío, la extensión no se cargó o no se intentó conectar. Tercero, el firewall de Windows: en instalaciones recientes bloquea conexiones locales al puerto del IDE sin avisar. Cuarto, el IDE: verifica que el listener esté activo antes de recargar la página. Y quinto, reinicia Apache desde el panel de XAMPP; editar php.ini no basta, el módulo se carga al arrancar el servidor.

La documentación oficial de Xdebug explica cada directiva con detalle, y los foros de XAMPP recogen infinidad de casos de rutas y puertos. No hay una configuración universal: depende de tu IDE, tu versión de PHP y cómo sirves el proyecto. Si algo no encaja, el log de Xdebug es el punto de partida más fiable antes de tocar más directivas.


Sobre el autor: DeiviSanzPlay — Soy David Sanz, Editor y Responsable de Contenidos. Mi trabajo consiste en revisar, corregir y mejorar los contenidos de este blog para que la información sea clara, útil y esté bien estructurada. Trabajo con diferentes temáticas y verticales, por lo que una parte importante de mi día a día es analizar cada contenido, cuidar la redacción y asegurar que el lector encuentre información de calidad. Me gusta prestar atención a los detalles. Desde una corrección de estilo hasta la organización de un artículo, todo cuenta cuando se trata de crear contenidos que realmente aporten valor y sean fáciles de entender. En este blog encontrarás artículos sobre diferentes temas y sectores. Mi responsabilidad es cuidar y supervisar cada contenido para que mantenga unos estándares de calidad y ofrezca una buena experiencia a quien nos visita. Porque para mí, un buen contenido no consiste únicamente en escribir. También hay que revisar, contrastar, ordenar y pensar en la persona que está al otro lado de la pantalla.

Perfiles verificables: Sitio web

Publicado el 29 de October de 2026. Última revisión: 29 de October de 2026.