IIS 500 Internal Server Error After web.config Change

A step-by-step guide to diagnosing and fixing IIS 500 errors after editing web.config, using detailed errors, Failed Request Tracing, and logs to pinpoint the broken setting.

Rao Aadil, India 7 min read

Confirm the symptom: it’s a web.config problem

A 500 after a web.config edit usually means the change introduced a configuration error. Confirm the failure is consistent and not transient: request the site with a browser and with a command-line client. In PowerShell, use Invoke-WebRequest or curl.exe (not the curl alias, which may be a different command on Windows):

Invoke-WebRequest -Uri http://localhost -UseBasicParsing
curl.exe -I http://localhost

If you also manage an nginx site on Linux, the equivalent is curl -I http://localhost. For an IIS site, the content root is normally under C:\inetpub\wwwroot\<site>, while an nginx server block might live in /etc/nginx/sites-available/ on Linux or C:\nginx\conf\ on Windows.

If the site returns HTTP 500 on every request, rule out other causes by restoring the previous web.config and requesting again. On Windows:

Copy-Item C:\inetpub\wwwroot\site\web.config.backup C:\inetpub\wwwroot\site\web.config -Force

The Linux equivalent for an nginx config is cp /etc/nginx/sites-available/site.conf.backup /etc/nginx/sites-available/site.conf. If reverting makes the 500 disappear, the web.config change is responsible.

A generic 500 hides the real error. IIS often returns a sub-status code that narrows it down. 500.19 means the configuration data itself is invalid (malformed XML, a locked section, or an invalid attribute); 500.21 indicates a module referenced in <modules> is missing or not installed; 500.22 points to an ASP.NET module invalid for the current managed pipeline mode. The detailed sub-status is visible in the response body when detailed errors are enabled, or in the IIS logs.

Doing this with brynko devOps Agent

Test configs before they go live, read the right log, and reload safely, on nginx, IIS or Tomcat.

“the site is throwing 502, it was fine yesterday”

It reads the real state of the server before it says anything, shows you the exact command, and waits for your approval. Windows and Linux, over the SSH access you already have.

Download for WindowsSee what else it does

Find the cause: which web.config setting broke the site

The fastest way to see the actual error is to enable detailed error messages temporarily. Edit the site's web.config and set the error mode:

<configuration>
  <system.webServer>
    <httpErrors errorMode="Detailed" />
  </system.webServer>
</configuration>

IIS Manager does the same: open the site, double-click Error Pages, click Edit Feature Settings, and choose Detailed errors. This shows the module and line number to anyone who can reach the site, so turn it off as soon as you have what you need. In production, enable detailed errors only for a local test or a short window with restricted access.

Failed Request Tracing (FREB) gives a full trace of the request pipeline—the most reliable way to identify the exact module and configuration line. In IIS Manager, select the site, open Failed Request Tracing Rules, and add a rule for status code 500. Trace all content, or at least the path that fails, and include the WWW Server, ASP, and ASP.NET providers. Trigger the request again; the newest trace log lands under %SystemDrive%\inetpub\logs\FailedReqLogFiles\W3SVC*. Open it. The trace shows each module that ran, and the module that fired the 500 includes an error description, a configuration line number, and often the exact property that is invalid.

IIS logs and Windows Event Viewer provide the same error without a full trace. Request logs are under %SystemDrive%\inetpub\logs\LogFiles\W3SVC*; search for the failed request by status code:

Get-ChildItem C:\inetpub\logs\LogFiles\W3SVC1\*.log | Select-String -Pattern " 500 "

The Event Viewer entry is under Windows Logs > Application, usually with a source of "IIS-W3SVC-WP" or "Microsoft-Windows-IIS-W3SVC". The event message often repeats the 500.19 sub-status and the configuration error.

Use appcmd or PowerShell to see the effective configuration and spot locked sections or invalid values:

appcmd list config "SiteName/"
Get-WebConfiguration -PSPath 'IIS:\Sites\SiteName' -Filter 'system.webServer/*'

Look for a section that failed to load or a property with an invalid value. If the error message mentions a locked section, compare the site's web.config with the same section in applicationHost.config or machine.config. System-level files often set overrideModeDefault="Deny" for sensitive sections such as system.webServer/modules.

Log-reading tools shorten this step: they find and tail the IIS log or event log entry for the failing request instead of forcing a manual search through several W3SVC log files. If you also manage nginx, the same approach applies: check /var/log/nginx/error.log on Linux or C:\nginx\logs\error.log on Windows, and use nginx -t to validate the nginx config before reloading.

Fix it: correct the web.config and restore the site

Open web.config and find the line the error from FREB or Event Viewer reports. Common causes of a 500 after an edit include:

  • Malformed XML: a missing closing tag, an unescaped &, or a stray character.
  • Invalid attribute values: a numeric setting that is out of range, or a path that does not exist.
  • Missing modules or handlers: a <modules> or <handlers> entry that refers to a DLL not installed on the server.
  • Duplicate keys in <appSettings>, <connectionStrings>, or other collections that do not allow duplicates.
  • Locked sections: editing system.webServer/modules or another section that is locked in applicationHost.config.
  • Invalid file paths: a rewrite rule, log directory, or virtual directory that points to a nonexistent location.

Correct the specific setting or remove the change. If the error is in a child web.config but the site root loads, the problem is often a configuration inheritance conflict: a setting in the parent site or in applicationHost.config overrides or locks the child. Use appcmd list config "SiteName/childApp" to see the merged configuration and identify where the conflicting value comes from.

Test again with Invoke-WebRequest or curl.exe. If the site still returns 500, go back to the error discovery step. Once the site responds, restart the IIS app pool to clear cached state:

Restart-WebAppPool -Name "YourAppPool"

If you manage nginx on the same server or a separate one, validate and reload nginx instead:

nginx -t && nginx -s reload

On Windows nginx, use the same command if nginx.exe is in your PATH, or the full path C:\nginx\nginx.exe -t && C:\nginx\nginx.exe -s reload. Management tools that run these operations through wrapper scripts remove the risk of a raw shell mistake and ensure the reload only happens after a successful test.

Keep a copy of the previous web.config before each change, and use version control. If a fix is not obvious, revert to the last known good version and apply changes incrementally, testing after each one.

Prevent it: validate web.config before it goes live

XML syntax errors are the easiest to catch before deployment. In PowerShell, use .NET's XML parser:

try {
    [xml](Get-Content C:\inetpub\wwwroot\site\web.config) | Out-Null
    Write-Host "web.config is well-formed XML"
} catch {
    Write-Error "Invalid XML: $_"
}

On Linux, use xmllint for an XML-based config file such as a web.config for a cross-platform app, or an nginx config (though nginx uses nginx -t for its own syntax):

xmllint --noout /path/to/web.config

IIS has no built-in pre-flight validation command like nginx -t. The only reliable pre-deployment checks are XML well-formedness and a staging environment that mimics production. Use IIS Manager's Configuration Editor to test changes on a staging site before applying them to production.

If you also manage nginx, a config validation tool runs nginx -t before any reload, catching syntax errors before they take effect. The same principle applies to IIS: validate the XML, review the diff, and deploy from source control with a rollback path.

General prevention: keep web.config in source control, review changes with a diff before applying, and use a deployment process that can roll back quickly. Test-Path verifies that any file paths in the config actually exist on the server before deployment:

Test-Path C:\inetpub\wwwroot\site\bin\SomeModule.dll

The Linux equivalent is test -f /path/to/file.

Related reading

Fix Your IIS 500 After web.config Edits

Read the exact error, correct the web.config line, and safely restart the site with the right tooling.

An AI teammate that builds, runs, fixes, deploys and operates your software, on your machine and on your servers. It reads the real state of your systems, shows you every command before it runs, and never touches production without your approval.

Works with Docker Docker Compose nginx IIS Tomcat MySQL / MariaDB PostgreSQL Redis Linux servers Windows servers AWS Jenkins Let's Encrypt Node.js Python Java .NET Go PHP
  • Every change asks first, and shows you the exact command or diff before it runs.
  • File edits are snapshotted and reversible. Deleted folders are archived, not removed.
  • Private key material never reaches the model, under any setting.
brynko devOps Agent
Web servers

the site is throwing 502, it was fine yesterday

read the error log checked the vhost listed ports

nginx is fine and so is your app. The error log repeats connect() failed (111: Connection refused) for upstream 127.0.0.1:8019, but nothing is listening there. Your container is bound to 9014. The port changed at the last deploy and the vhost still points at the old one.

point nginx at the right one

one line in the vhost nginx -t passed reloaded

Done. I changed the upstream port in your site config, tested it before applying, and reloaded. The site is returning 200 again.

Fixed · 200 OK

Free download for Windows. 300 free credits on signup, no card, no subscription.

Questions

Common questions

How do I see the detailed error instead of the generic 500 page?
Edit web.config and add `<httpErrors errorMode="Detailed" />` inside `<system.webServer>`, or in IIS Manager go to Error Pages > Edit Feature Settings and select Detailed errors. The detailed response will include the sub-status code, module, and line number. Turn it off after diagnosis because it can leak internals.
Which line in web.config caused the 500 error?
Enable detailed errors or Failed Request Tracing and reproduce the request. The error page or FREB trace will show the module and a configuration line number. Open web.config at that line and correct the setting.
How do I turn on Failed Request Tracing for a specific status code?
In IIS Manager, select the site, open Failed Request Tracing Rules, and add a rule. Specify the status code 500 (or the sub-status like 500.19) and choose the providers to trace. Trigger the request, then open the trace log in %SystemDrive%\inetpub\logs\FailedReqLogFiles\W3SVC*.
Why does IIS show 500.19 specifically, and what are the subcodes?
500.19 means the configuration data itself is invalid—the XML is malformed, a section is locked, or an attribute has an invalid value. Subcodes such as 500.19.0, 500.19.1, and 500.19.2 further narrow whether the error is during startup, while reading a collection, or from a locked section. The exact subcode appears in detailed errors or Event Viewer.
How can I roll back my web.config change quickly?
Keep a backup copy of web.config before editing, for example web.config.backup. Restore it with `Copy-Item web.config.backup web.config -Force` and request the site again. If you use source control, redeploy the previous commit.
What are the most common web.config settings that cause 500 errors?
Malformed XML, invalid attribute values, missing modules or handlers referenced in `<modules>`/`<handlers>`, duplicate keys in `<appSettings>` or `<connectionStrings>`, locked sections, and file paths that do not exist. Any of these can produce a 500, often with sub-status 500.19 or 500.21.
Is there a way to validate web.config syntax before copying it to the server?
Yes, at least for XML well-formedness. Parse the file with PowerShell `[xml](Get-Content web.config)` or `xmllint` on Linux. IIS has no built-in pre-flight validation like nginx -t, so use a staging server or IIS Manager's Configuration Editor for deeper checks.
Can I use appcmd to see the effective configuration and identify the problem?
Yes. `appcmd list config "SiteName/"` shows the merged configuration for the site, including inherited and locked sections. PowerShell's `Get-WebConfiguration` provides the same data and can filter to a specific section.

Keep reading