Trace.axd information disclosure means an ASP.NET application has application-level tracing turned on and lets remote visitors open the trace viewer, which lists recent requests with their cookies, form fields, headers and server paths. Fix it by setting <trace enabled="false" localOnly="true" /> in web.config, removing Trace="true" from pages, and confirming /trace.axd returns 403 or 404 remotely.
What the scanner is actually detecting
Trace.axd is the built-in trace viewer of classic ASP.NET (System.Web on the .NET Framework). The framework’s root web.config maps it as an HTTP handler for every application, so the URL exists on any IIS site running ASP.NET Framework code, whether or not tracing is on. The scanner checks whether that viewer actually returns trace data to a remote, unauthenticated client.
| Scanner | Finding title | ID and rating |
|---|---|---|
| Nessus | Microsoft ASP.NET Application Tracing trace.axd Information Disclosure | Plugin 10993, Medium, CGI abuses family |
| Burp Scanner | ASP.NET tracing enabled | Issue type 0x00100280, High |
Plugin 10993 is a remote check: it judges what each web server returns over HTTP, not your configuration files. PortSwigger’s advice also covers page-level tracing, which appends the same trace tables to individual pages.
The viewer is easy to recognize: an “Application Trace” heading, a “Physical Directory:” line showing the application’s path on disk, and a “Requests to this Application” table with “View Details” links. Tenable’s description refers to the last 50 requests; the number actually held depends on requestLimit, which defaults to 10 and is capped at 10,000.
How serious is it?
The exposure needs two things at once: tracing enabled (enabled="true") and remote viewing allowed. The default for localOnly is true, so a remote hit usually means someone set localOnly="false" while debugging and never reverted it.
Each “View Details” page shows what Microsoft documents as part of the trace: the session ID, request and response cookies (including session and forms authentication cookies), headers, the form collection, query string values and server variables. On a login or payment page, the form collection holds whatever users submitted, which can include password fields. Anyone who reaches the viewer can read other users’ cookies and potentially take over their sessions, and the physical path and server variables help with follow-on attacks.
How much leaks depends on the buffer. With the default mostRecent="false", ASP.NET stops recording once requestLimit is reached, so the viewer may only show a few old requests. With mostRecent="true" it keeps rolling and exposes fresh traffic continuously. Either way, treat it as a real exposure, not scanner noise.
How to confirm it on the host
Start from outside the server, the way the scanner does. Test the site root and every application path listed in the finding:
curl -sk -o /dev/null -w "%{http_code}n" https://app.example.com/trace.axd
curl -sk https://app.example.com/trace.axd | grep -iE "Application Trace|Requests to this Application"
curl -sk -o /dev/null -w "%{http_code}n" https://app.example.com/appname/trace.axd
How to read the result:
- 200 with “Application Trace”: exposed. This is the finding.
- 403: the handler refused a remote client because
localOnlyis true or the server runs in retail mode. Not exposed remotely, although tracing may still be on. - 500 with a “Trace Error” or generic error page: tracing is disabled but
localOnlyis false. Not exposed. - 404: the path is blocked, or the site is not running ASP.NET Framework.
On the IIS server, from an elevated prompt, list every configuration location that turns tracing on, then check the effective setting for a specific application:
%windir%system32inetsrvappcmd.exe search config /section:system.web/trace /enabled:true
%windir%system32inetsrvappcmd.exe list config "Default Web Site/" /section:system.web/trace /config:*
Page directives and inline code can enable tracing regardless of web.config, so search content folders too (PowerShell). Code compiled into DLLs will not show up; check source control for that.
$roots = & "$env:windirsystem32inetsrvappcmd.exe" list vdirs /text:physicalPath |
ForEach-Object { [Environment]::ExpandEnvironmentVariables($_) }
Get-ChildItem -Path $roots -Recurse -Include web.config,*.aspx -ErrorAction SilentlyContinue |
Select-String -Pattern '<traces[^>]*enableds*=s*"true"', 'bTraces*=s*"true"', 'Trace.IsEnableds*=s*true' |
Select-Object Path, LineNumber, Line
How to fix it
1. Disable tracing in the application web.config
Set the trace element explicitly under <system.web>. Keeping localOnly="true" means that if someone switches enabled back on later, remote clients get a 403 instead of data.
<configuration>
<system.web>
<trace enabled="false" localOnly="true" pageOutput="false" />
</system.web>
</configuration>
The same change with appcmd, which writes to the web.config at the path you name:
%windir%system32inetsrvappcmd.exe set config "Default Web Site/" /section:system.web/trace /enabled:false /localOnly:true
A web.config lower in the tree can override the setting for its own path, so check child applications with the search config command above.
2. Remove page-level tracing
A Trace attribute in a page’s @ Page directive overrides the application setting for that page. Remove it from directives such as <%@ Page Language="C#" Trace="true" %> (or set it to false), delete any Trace.IsEnabled = true statements in code, then rebuild and redeploy.
3. Stop deployments from bringing it back
If web.config is produced by your build, fix the source as well as the server copy. In a Web.Release.config transform, removing the element lets the secure defaults (enabled="false", localOnly="true") apply:
<system.web>
<trace xdt:Transform="Remove" />
</system.web>
4. Enforce it server wide with retail mode (optional)
Retail mode disables trace output for every application on the server and overrides application web.config files. It can only be set in machine.config. Add it inside <system.web> in each framework folder your applications use, for example %windir%Microsoft.NETFramework64v4.0.30319Configmachine.config and its 32-bit Framework counterpart:
<system.web>
<deployment retail="true" />
</system.web>
Read the side effects in the rollback section before doing this on a shared server.
5. Block the URL with IIS request filtering (defense in depth)
A hidden segment makes IIS return 404 (logged as substatus 404.8) for any URL with a trace.axd path segment, before ASP.NET sees the request. At server level:
%windir%system32inetsrvappcmd.exe set config /section:system.webServer/security/requestFiltering /+"hiddenSegments.[segment='trace.axd']" /commit:apphost
This also blocks local viewing, so leave it off development servers.
6. Apply the change
Saving an application’s web.config restarts that application automatically, which also discards the trace data ASP.NET was holding in memory. After a machine.config change, or whenever you want a clean restart, recycle the pool:
%windir%system32inetsrvappcmd.exe recycle apppool "MyAppPool"
# or, in PowerShell with the WebAdministration module
Restart-WebAppPool -Name "MyAppPool"
How to verify the fix and rescan
- From a machine outside the server, rerun the curl commands against every hostname, port and application path in the finding. Expect 403 (localOnly or retail mode) or 404 (hidden segment), and no “Application Trace” text in the body.
- If the site sits behind a load balancer, test the public address the scanner used and each backend node, since one node may still carry the old web.config.
- Rerun
appcmd search config /section:system.web/trace /enabled:true. It should return no locations. - Rescan. In Nessus you can limit the scan to plugin 10993 on the affected hosts; in Burp, rerun an active scan against the same URLs.
DAST and network scanners catch the live endpoint; static analysis or code review catches a Trace="true" directive before it ships. For which tool should own which check, see how SAST, DAST and SCA differ.
What can break and how to roll back
Turning tracing off does not change how the application serves users. You lose a debugging aid, and the optional steps go further:
- Developers lose remote trace.axd and must view it on the server itself or in a non-production environment.
- Retail mode forces the
customErrorsmode to On, disables detailed error messages for remote users and disables debug capabilities for every application on the server. Keepdebug="false"in application web.config files as well, because retail mode does not override the request timeout change that debug mode makes. - The hidden segment blocks trace.axd for everyone, including administrators on the server itself.
Before editing, run appcmd add backup to snapshot server-level IIS configuration, and copy each web.config and machine.config you touch, since appcmd backups do not include them. To roll back:
- web.config: restore the previous file, or rerun the appcmd
set configcommand with the old values. - Retail mode: remove the
<deployment retail="true" />line and recycle the application pools. - Hidden segment: run
%windir%system32inetsrvappcmd.exe set config /section:system.webServer/security/requestFiltering /-"hiddenSegments.[segment='trace.axd']" /commit:apphost, or restore the appcmd backup (restoring stops the server and reverts all global configuration).
Common false positive reasons
- Catch-all responses. A single-page application, an ASP.NET Core app or a custom error handler may return 200 with a normal page for any path. If the body has no “Application Trace” heading and no request list, the viewer is not exposed; keep the response as evidence.
- Error page matched on text. The 403 or 500 “Trace Error” page mentions trace.axd by name. A check that matches on text alone can mistake it for the viewer, so look at the status code and the body.
- Scanner on the server itself. A scan launched from the web server counts as local, so it sees the viewer even with
localOnly="true". Tracing is still on and worth disabling, but it is not remotely exposed. - Wrong node or stale result. A load balancer sent the scanner to a node you did not change, or the report predates the fix.
FAQ
Is localOnly=”true” enough on its own?
It stops ordinary remote viewing, but ASP.NET decides what is local from the connection’s source address. A reverse proxy on the same server, such as IIS Application Request Routing forwarding to a local site, can make remote visitors look local. Set enabled="false" in production and keep localOnly="true" as a backstop.
Is this the same as the HTTP TRACE method finding?
No. HTTP TRACE is a request method that echoes the request back and is controlled by the web server. Trace.axd is an ASP.NET page that shows stored diagnostic data. Each has its own fix.
Does this turn off IIS Failed Request Tracing?
No. Failed Request Tracing is a separate IIS feature with its own configuration that writes log files on the server. The system.web/trace setting only controls ASP.NET tracing and the trace.axd viewer.
Does this apply to ASP.NET Core?
No. Trace.axd belongs to ASP.NET on the .NET Framework, and ASP.NET Core has no trace viewer at that path. A hit on a Core application is almost always a catch-all response, but check the body before closing it.
Tracking this finding across many hosts
On a large IIS estate this finding appears per site and per application path, often from both Nessus and Burp. SITEY, a self-hosted vulnerability management platform, imports findings from 16 scanners (Nessus results through an uploaded .nessus export) and merges duplicates within each scanner, so a Nessus row and a Burp row for the same URL stay separate. Both Nessus and Burp findings can be re-tested individually after the change, and AI-drafted, host-specific remediation scripts run through SITEY agents only after human approval.
Sources
- Tenable: Microsoft ASP.NET Application Tracing trace.axd Information Disclosure (plugin 10993)
- PortSwigger: ASP.NET tracing enabled
- Microsoft Learn: trace element (ASP.NET Settings Schema)
- Microsoft Learn: deployment element (ASP.NET Settings Schema)
- Microsoft Learn: IIS Request Filtering hiddenSegments