Use of special characters like '%' ‘.’ and ‘:’ in an IIS URL

There are multiple times that we get questions about % and other special characters in the URL and what the expected behavior is in IIS. The behavior in IIS is very deterministic when it comes to these special characters, but to explain the behavior we will need to delve a little bit into both URL canonicalization and the different stages of request processing in IIS.

Usage of special characters in the URL first and foremost depends on the portion of the URL it is being used in. Here is a sample URL., where
http:// is the protocol specifier is the hostname where the port specification is optional
application1/foo.aspx is the request URI or the URI path
var1=1&var2=2 is the query string.

IIS does not perform any checks on the query string and considers this portion as a blob and does no encoding processing and is as such not very interesting for this discussion. Both http.sys (driver mode HTTP parser) and IIS considers the query string opaque. The only exception to this are filters like Urlscan, Request Filtering etc. These may additionally block characters depending on their configuration.

The request URI (URI path) above is the most interesting and there are several layers of validation that happen, the first being by http.sys, the second by IIS request filtering, third by IIS core, and then by any handler (like ASP.NET).

Layer 1: Http.sys (blocked characters = ‘%’)

Http.sys parses URLs in accordance with RFC 2396. You can however configure exceptions using the AllowRestrictedChars registry setting documented in KB 820129. This configuration will never allow a bare ‘%’ character though because it is expressly forbidden in the RFC section 2.4.2 which says:

“Because the percent "%" character always has the reserved purpose of being the escape indicator, it must be escaped as "%25" in order to be used as data within a URI. Implementers should be careful not to escape or unescape the same string more than once, since unescaping an already unescaped string might lead to misinterpreting a percent data character as another escaped character, or vice versa in the case of escaping an already escaped string”

Layer 2: IIS request filtering (blocked characters = ‘%25’, ‘.’, ‘%2b’)

IIS request filtering checks for a couple of things by default and the behavior is controlled by configuration. You can reference the following articles for details, Use Request Filtering and Using Urlscan. This filtering potentially processes both the URI path and the query string and could disallow things like:

  1. Double escaping which involves the use of %25, which is an encoded ‘%’ sign. Also, ‘+’ characters are considered and encoded space even though it does not have a % in the beginning. So encoding this as %2b will also trigger this rule.
  2. Non-ASCII high bit characters.
  3. Dot in the URI path, where a request that contains a dot other than that for the resource extension will be rejected. Eg: http://foo/a.b/bar.aspx

Layer 3: IIS core (blocked characters = ‘:’)

There are no restrictions per se in IIS core, but there are a couple of things it does with physical file mappings that are produced from URL mappings.So for handlers like the static file handler that will serve out files, we will check for file paths containing the ‘:’ character to avoid ::$DATA like security issues that occurred in MS98-003 and MS10-065.

Layer 4: Handlers

This part depends on what handlers are being used in your request processing. So if you are using ASP.NET for instance, it could trigger request validation that could additionally block characters in both the URI path and the query string. If you are using Sharepoint, they have their own list of blocked characters as mentioned in KB 905231.


  • Nazim, excellent information. I have a question though.. reading up on the URI Syntax RFCs, I noticed that RFC3986 is the current one, which obsoletes RFC2396. Is HTTP.SYS losing anything here by not implementing the most recent RFC for this?

  • @somu76: RFCs supersede each other, but for the relevant bits in this discussion, nothing has changed between the two RFCs. I should have quoted the latest one, my mistake.

Comments have been disabled for this content.