There's a lot of misinformation and/or out-of-date solutions regarding error handling in IIS 7+. The main things to understand are:-
IIS 7+ introduced a new way of handling custom error pages.
Most people are using hodge-podge solutions involving all of customErrors, httpErrors the Application_Error handler, and often setting requestValidationMode="2.0" on the httpRuntime property and/or disabling request validation entirely! This makes it really difficult to use other people's solutions because any and all of these can affect the behaviour. I had a quick search around and I found several semi-duplicates without accepted answers, probably for this reason.
The reason that these two errors give you different behaviour is that they occur at different stages in the request pipeline. The customErrors node in your web.config interacts with errors "inside" your application, while request validation takes place "outside" your application. IIS rejects the dangerous request before it gets to your application code, and so your customErrors replacement doesn't happen.
Ideally you want a solution with as few moving parts as possible. IIS7 gives us a new way to specify error page replacement at the IIS level instead of at the application level - the httpErrors node. This lets us catch all our errors in one place:-
<httpErrors errorMode="Custom" existingResponse="Replace">
<error statusCode="400" responseMode="ExecuteURL" path="/ServerError.aspx"/>
<error statusCode="403" responseMode="ExecuteURL" path="/ServerError.aspx" />
<error statusCode="404" responseMode="ExecuteURL" path="/PageNotFound.aspx" />
<error statusCode="500" responseMode="ExecuteURL" path="/ServerError.aspx" />
If you care about SEO (and you should!), you still have to make sure that your controller/page sets an appropriate status code:-
this.Response.StatusCode = 500; // etc.
You should remove your customErrors node entirely. It is normally used for backwards-compatibility. You should also ensure that requestValidationMode is not set on the httpRuntime node.
This should catch most errors (excluding, obviously, errors in parsing your web.config!)
Note: in your case, if you want to set defaultPath on the httpErrors node, you'll get a lock violation because of ApplicationHost.config settings. You can either do as I did and just set path individually for the error codes you care about, or you can have a look at unlocking the node:-
My intuition is that it's more trouble than it's worth in low-control environments like Azure App Service / Azure Web Sites. You might as well set the paths for individual status codes.
By far this is the most detailed and perfect response that I've seen in years. Thank you very much!!! It's working perfectly!!!
I'm back with one more question: it seems that I have configured in my web.config a HttpModule (ImageProcessorModule) which intercepts all my calls (is quite normal) but when I'm dealing with the A potentially dangerous Request.RawUrl value was detected from the client, I see the error in clear text and no 500 custom error page shows up; though, if I'm commenting out this module to not intercept the requests, I see the custom error page... What am I missing? Why the system doesn't display the custom error message though the web.config says so?! Thank you very much!!!
You might be best posting that as a separate question with more info. I'm aware of cases where bugs in httpModules have caused problems with custom errors, but I'd need more to go on before I could narrow it down.