Skip to content

Digging Deeper

Debug Panel

The Debug Panel is a fixed bar that Wheels appends to every HTML response in development mode. It surfaces request details, execution timing, parameters, environment configuration, installed packages, and links to built-in tools — without leaving the page you are working on. The panel is rendered by vendor/wheels/events/onrequestend/debug.cfm and runs at the very end of the request lifecycle, after your controller and views have finished.

You’ll learn:

  • When the debug bar appears and how to control it
  • What each tab shows and which configuration drives it
  • How to read the timing breakdown to find bottlenecks
  • Where to find package and plugin status
  • How to access developer tools from the bar

The bar only renders for standard full-page HTML requests. It is automatically suppressed for any of the following:

  • AJAX requests (X-Requested-With: XMLHttpRequest)
  • HTMX requests (HX-Request)
  • Turbo Frame requests (Turbo-Frame)
  • Fetch requests (X-Fetch: true)
  • Requests that produce structured output (?format=json, xml, csv, or pdf)

This means AJAX-driven partial updates, Turbo Streams, API endpoints, and CSV exports all receive clean responses with no injected HTML.

The bar respects the showDebugInformation application setting. By default, Wheels sets this to true only in the development environment and false in every other environment (testing, maintenance, and production). You can override it explicitly in config/settings.cfm (or an environment-specific file such as config/production/settings.cfm):

config/settings.cfm
<cfset set(showDebugInformation=true)>

Setting showDebugInformation=false removes the bar entirely — no HTML is injected into responses.

Do not put this override in config/environment.cfm. That file only selects the environment: it is included before the framework’s own defaults are applied, so any other set() call placed there is silently overwritten.

When active, the bar appears as a slim strip pinned to the bottom of the browser window. Tabs are arranged from left to right: a Wheels logo (doubles as the Request tab toggle), then Request, Timing, Params, Environment, and optionally Tools. The right side shows the current git branch (when the web root — public/ — contains a .git directory), the framework version, and a one-click reload link when no reload password is configured.

The collapsed Wheels debug bar pinned to the bottom of a browser window: Wheels logo, Posts.index controller.action, timing badge, Params, Development environment, and Tools tabs, with Wheels 4.0.0-SNAPSHOT version on the right

Clicking any tab opens a panel above the bar. Clicking the same tab again closes it. The minimize button (×) collapses the bar to a small “Debug” button in the corner, persisted via sessionStorage so it stays collapsed as you navigate.

The Request tab label shows controller.action for the current request. Clicking it opens a key-value panel with:

FieldDescription
RouteNamed route that matched this request (omitted when no named route matched)
ControllerController name
ActionAction name
KeyURL key segment, if present
HTTP MethodGET, POST, PUT, DELETE, etc.
URLFull URL for this request
ApplicationValue of applicationName from Application.cfc
Data SourceThe configured dataSourceName
DB AdapterThe active database adapter (MySQL, PostgreSQL, SQL Server, etc.) — omitted when the adapter name is not detected
URL RewritingCurrent URL rewriting mode

Request panel open showing the key-value table: Route (root), Controller (Posts), Action (index), HTTP Method (GET), URL, Application (blog), Data Source (blog), DB Adapter (SQLiteModel), URL Rewriting (On)

Use this panel to confirm that routing resolved the way you intended, that the correct data source is in use, and that URL rewriting is configured as expected.

The Timing tab label shows the total request duration in milliseconds, color-coded by threshold:

  • Green — under 100 ms
  • Yellow — 100–499 ms
  • Red — 500 ms or more

Opening the panel shows a horizontal bar chart breaking down where time was spent. Each framework phase (setup, requestStart, beforeFilters, action, afterFilters, view, requestEnd) that recorded time is sorted by duration descending and rendered as a proportional colored bar — the bar’s width represents the phase’s share of total time, and the raw milliseconds are printed inside it. No percentage figure is displayed.

Timing panel showing "Execution Timing — 9ms total" in the header with horizontal bar chart for action and view phases, color-coded and sorted by duration

The chart is most useful for spotting where a slow request spends its time: if action dominates the total, look at the model calls and SQL your action triggers; if view dominates, look at the rendering work in your templates and partials.

The Params tab shows the count of extra request parameters as a badge. Opening the panel displays a table of every parameter that was available to the action, excluding the routing parameters (controller, action, key, route) and fieldnames.

ColumnDescription
NameParameter key, lowercased
ValueThe value; complex values (structs, arrays) are JSON-encoded
Typestring or json

Params panel showing the Request Parameters table with Name, Value, Type columns — four rows for status=published, category=tech, author=peter, page=2 — all string type

This is the fastest way to confirm that form fields, query string values, and nested structs arrived the way you expected before you reach for a writeDump call.

The Environment tab label shows the current environment name (Development, Testing, etc.) with a color-coded dot: green for development, orange for testing, yellow for maintenance, and red for production. The panel is divided into subsections based on what is enabled.

Always shown. Lists the environment, the git branch (when the web rootpublic/ — contains a .git directory; in a standard app layout the .git directory lives at the project root, so the row does not appear), the Wheels version, the CFML engine and version, and the host name (when available).

When a reloadPassword is configured and allowEnvironmentSwitchViaUrl resolves to true, the environment name is followed by quick-switch links for the other environments. Clicking one prompts for the reload password — it is never embedded in the page — and then issues the documented ?reload=<environment>&password=<your password> request. When no reloadPassword is set, or switching via the URL is disallowed, the links do not render because the switch cannot work. See #3060.

You can also switch environments manually: set a reloadPassword in config/settings.cfm and request ?reload=<environment>&password=<your password>. The switch is additionally gated by the allowEnvironmentSwitchViaUrl setting: an explicit boolean you set is honored in every environment, and when unset it defaults to false in production, testing, and maintenance and true everywhere else. Switching to the environment you are already in is a no-op, and after a successful switch the redirect strips the reload parameters from the URL.

Environment & Configuration panel showing Application section with Environment (Development with green dot), Wheels Version, CFML Engine (Lucee 7.0.0.395), Host Name; Packages section listing wheels-basecoat; and Plugins (Legacy) "No plugins installed."

Shown when enablePackagesComponent is true (the default in Wheels 4.0+).

Installed packages lists every package found in vendor/ and loaded by PackageLoader.cfc:

ColumnDescription
PackagePackage name from package.json
VersionSemver version string
DescriptionOne-line description

If any package failed to load (manifest error, missing dependency, CFC exception), a red error list appears below the installed table with the package name and the exception message.

Packages section of the Environment panel — Installed table showing wheels-basecoat 3.0.0-rc.1 with version and description columns

To browse what is available to install — and discover first-party packages such as wheels-hotwire, wheels-sentry, or wheels-i18n — open the Packages tool page at /wheels/packages (linked from the Tools tab) or run wheels packages list from the CLI. Both read the same 24-hour cached registry index; run wheels packages registry refresh if you need up-to-the-minute data.

Shown when enablePluginsComponent is true. Lists plugins installed in the legacy plugins/ directory with their names and versions. Legacy plugins are the Wheels 3.x predecessor to packages — if you are on a fresh 4.x app, this section is likely empty.

If any plugin warnings exist, a red Warnings subsection appears beneath the plugin table:

  • Incompatible plugins — plugins flagged as incompatible with the current Wheels version (controlled by showIncompatiblePlugins)
  • Dependent plugins — plugins whose dependencies are not satisfied
  • Version mismatches — plugins that require a different version of another plugin than what is loaded
  • Mixin collisions — cases where two plugins defined the same method name on the same mixin target; the later-loaded plugin wins

Shown when enablePublicComponent is true (the default in development). Opens a grid of link cards, each opening in a new tab:

CardLinks to
System Info/wheels/info — Wheels internals, environment variables, loaded settings
Routes/wheels/routes — all registered routes with HTTP method, path, and handler
API Docs/wheels/api — generated API documentation
Guides/wheels/guides — the documentation site
Tests/wheels/app/tests — the test runner
Migrator/wheels/migrator — the database migration UI (shown when enableMigratorComponent is true)
Packages/wheels/packages — the package browser (shown when enablePackagesComponent is true)
Plugins/wheels/plugins — the legacy plugin list (shown when enablePluginsComponent is true)

Tools panel showing the Developer Tools link-card grid: System Info, Routes, API Docs, Guides, Tests, Migrator, Packages, and Plugins

All settings are applied in config/settings.cfm or an environment-specific file under config/ (not config/environment.cfm, which only selects the environment — see Enabling and disabling):

config/settings.cfm
<cfset set(showDebugInformation=true)>
<cfset set(enablePackagesComponent=true)>
<cfset set(enablePluginsComponent=true)>
<cfset set(showIncompatiblePlugins=true)>
<cfset set(enablePublicComponent=true)>
<cfset set(enableMigratorComponent=true)>
SettingDefaultEffect
showDebugInformationtrue in development, false elsewhereShow or hide the debug bar entirely
enablePackagesComponenttrueShow the Packages subsection in the Environment panel and the Packages link in Tools
enablePluginsComponenttrueShow the Plugins (Legacy) subsection and the Plugins link in Tools
showIncompatiblePluginstrueInclude incompatible-plugin warnings in the Warnings subsection
enablePublicComponenttrue in development, false elsewhereShow the Tools tab and its link grid
enableMigratorComponenttrueShow the Migrator link in the Tools panel

Three settings let requests from specific source IPs see the debug bar in non-development environments, overriding the “false elsewhere” defaults above on a per-request basis:

SettingDefaultEffect
allowIPBasedDebugAccessfalseMaster switch for the per-request IP override
debugAccessIPs[]Array of allowed client IP addresses (exact match)
debugAccessTrustProxyfalseWhen true, use the rightmost X-Forwarded-For entry as the client IP — only enable this behind a trusted reverse proxy, since the header is otherwise client-controlled

When allowIPBasedDebugAccess is true and the request comes from an IP listed in debugAccessIPs, Wheels re-enables showDebugInformation, enablePublicComponent, and showErrorInformation for that request only — so the debug bar (including the Tools tab) renders even in production. The /wheels/* tool pages themselves still return 404 outside development: the allowlist described in the note above is environment-based and is not bypassed by the IP override. Requests from any other IP are unaffected.