How to troubleshoot HTTP 400 errors
After sending an HTTP request to an IIS server, an HTTP client (such as Internet Explorer) may display the following type of error message in the browser window:
If Internet Explorer’s Friendly HTTP Error Messages option is turned off, the error may resemble the following:
In these scenarios, IIS has rejected the client’s HTTP request because it did not meet the server’s parsing rules, or it exceeded time limits, or failed some other rule that IIS requires incoming requests to adhere to. IIS sends the HTTP 400 – Bad Request status back to the client, and then terminates the TCP connection.
When troubleshooting an HTTP 400 condition, it is important to remember that the underlying problem is that the client has sent a request to IIS that breaks one or more rules that HTTP.sys is enforcing. With that in mind, you will want to see exactly what the client is sending to IIS; to do this, capture a network trace of the client sending the bad request. You can analyze the trace to see the raw data that the client sends to IIS, and to see the raw response data that IIS sends back to the client. You can also use an HTTP sniffer tool called Fiddler; this is a great tool as it allows you to see the HTTP headers even if the client and server are communicating over SSL.
The next data item you will want to use is the httperr.log file. Beginning in IIS 6.0, the HTTP.sys component handles incoming HTTP requests before they are passed along to IIS, and is the component responsible for blocking requests that don’t meet the IIS requirements. When HTTP.sys blocks the request, it will log information to its httperr.log file concerning the bad request.
NOTE: For more information on the HTTP API error logging that HTTP.sys provides, see the following article:
Error logging in HTTP API
It is technically possible, although not very likely, that a client will receive an HTTP 400 response which does not have an associated log entry in the httperr.log. This could happen if an ISAPI filter or extension or an HTTP module in IIS sets the 400 status, in which case you could look at the IIS log for more information. It could also happen if an entity between the client and the server, such as a proxy server or other network device, intercepts a response from IIS and overrides it with its own 400 status and/or “Bad Request” error.
Following is an example of an HTTP 400 scenario, where a client sends a bad request to IIS and the server sends back an HTTP 400 – Bad Request message.
When the client sends its request, the browser error it gets back looks like this:
Capturing a network trace of the request and response, we see the following raw request/response headers:
HTTP: GET Request from Client
HTTP: Request Method =GET
HTTP: Uniform Resource Identifier =/1234567890123456789012345678901234567890/time.asp
HTTP: Protocol Version =HTTP/1.1
HTTP: Accept-Language =en-us
HTTP: UA-cpu =x86
HTTP: Accept-Encoding =gzip, deflate
HTTP: Host =iisserver
HTTP: Connection =Keep-Alive
HTTP: Data: Number of data bytes remaining = 14 (0x000E)
HTTP: Response to Client; HTTP/1.1; Status Code = 400 - Bad Request
HTTP: Protocol Version =HTTP/1.1
HTTP: Status Code = Bad Request
HTTP: Reason =Bad Request
HTTP: Content-Type =text/html
HTTP: Date =Wed, 28 Jan 2009 20:36:36 GMT
HTTP: Connection =close
HTTP: Content-Length =44
HTTP: Data: Number of data bytes remaining = 63 (0x003F)
You’ll notice that the response headers don’t tell us as much as the error message in the browser. However if we look at the raw data in the response body, we’ll see more:
00000030 48 54 HT
00000040 54 50 2F 31 2E 31 20 34 30 30 20 42 61 64 20 52 TP/1.1.400.Bad.R
00000050 65 71 75 65 73 74 0D 0A 43 6F 6E 74 65 6E 74 2D equest..Content-
00000060 54 79 70 65 3A 20 74 65 78 74 2F 68 74 6D 6C 0D Type:.text/html.
00000070 0A 44 61 74 65 3A 20 57 65 64 2C 20 32 38 20 4A .Date:.Wed,.28.J
00000080 61 6E 20 32 30 30 39 20 32 30 3A 33 36 3A 33 36 an.2009.20:36:36
00000090 20 47 4D 54 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E .GMT..Connection
000000A0 3A 20 63 6C 6F 73 65 0D 0A 43 6F 6E 74 65 6E 74 :.close..Content
000000B0 2D 4C 65 6E 67 74 68 3A 20 34 34 0D 0A 0D 0A 3C -Length:.44....<
000000C0 68 31 3E 42 61 64 20 52 65 71 75 65 73 74 20 28 h1>Bad.Request.(
000000D0 48 65 61 64 65 72 20 46 69 65 6C 64 20 54 6F 6F Header.Field.Too
000000E0 20 4C 6F 6E 67 29 3C 2F 68 31 3E 01 02 03 04 05 .Long)</h1>.....
000000F0 05 06 0E 94 63 D6 68 37 1B 8C 16 FE 3F D5 ....c.h7....?.
The next step is to look at the httperr.log file for the entry that corresponds to the bad request:
#Software: Microsoft HTTP API 1.0
#Date: 2009-01-28 20:35:02
#Fields: date time c-ip c-port s-ip s-port cs-version cs-method cs-uri sc-status s-siteid s-reason s-queuename
2009-01-28 20:36:36 clientIP:port serverIP:port HTTP/1.1 GET /1234567890/time.asp 400 - FieldLength –
In this scenario, the Reason field in the httperr.log file gives us the exact information we need to diagnose the problem. We see here that HTTP.sys logged FieldLength as the reason phrase for this request’s failure. Once we know the reason phrase, we can use the Error Logging in HTTP API article mentioned above to get its description:
FieldLength : A field length limit was exceeded.
So at this point we know from the browser error message and the HTTP API error logging that the request contained data in one of its HTTP headers that exceeded the allowable length limits. For the purpose of this example, I made the HTTP: Uniform Resource Identifier header purposefully long, by sending a request to IIS for a long URL, /1234567890123456789012345678901234567890/time.asp.
The final stage of troubleshooting this example is to use the following article to see the HTTP.sys registry keys and default settings for IIS:
Http.sys registry settings for IIS
Since we know the reason phrase and error are suggesting a header field length exceeding limits, we can narrow our search in KB820129 as such. The prime candidate here is:
Sets an upper limit for each header. See MaxRequestBytes. This limit translates to approximately 32k characters for a URL.
To reproduce this error, I had created the MaxFieldLength registry key and given it a value of 2. Since the requested URL had a HTTP: Uniform Resource Identifier header field with more than 2 characters, the request was blocked.
When troubleshooting HTTP 400 conditions, first remember that the client has sent a request to IIS that breaks one or more rules that HTTP.sys is enforcing. Then, gather a network trace of the request/response, to see the raw data the client is sending to the server, and the error data the server sends back to the client. Next, get the httperr.log data for the failed request. Finally, use the error message in the browser, the network trace data, and the httperr.log data to pinpoint the failure reason as per KB820729. It is possible that HTTP.sys can be configured to allow the request (although doing so may lower the security level of your IIS server), so check KB820129 to verify.
Following are links to useful articles for troubleshooting some known HTTP 400 scenarios.
Error message when an Outlook Web Access user tries to access a mailbox in Exchange Server 2003
Error message when you use a mobile device to access an ASP.NET Web site that is hosted in IIS 6.0: "HTTP 400 - Bad Request"
You receive a “HTTP 400 - Bad Request" error message when you try to use the Monitoring and Reporting snap-in or the Backup snap-in on a Windows Small Business Server 2003-based computer
Error Message: HTTP/1.1 Error 400 - Bad Request