I was experiencing this same behavior.
First, you set your web.config to redirect for a 404, like:
<error redirect="~/Sitefinity/nopermissions.aspx" statusCode="403" />
<error redirect="~/PageNotFound.aspx" statusCode="404" />
Now if you go to a page with an ASPX extension like:
you see the 404 error page as it should be rendered (with one issue, see below).
If you go to a page without an extension:
you get whatever IIS is configured to show, because SF isn't handling this one.
However, if you look at the source code, each page is identical except for the form tag action.
The problem is that in the case where I look for a non-aspx page, SF doesn't handle the response, IIS does. IIS doesn't send a 302 redirect. I suspect it's a server.transfer rather than a redirect. It would be great if that could be changed.
At any rate, if IIS handles it, the final URL as far as the browser is concerned is still the original URL, and Sitefinity rewrites the stylesheet URL (as well as any others with the ~ token) to be relative to what it thinks is the current folder, the root. But the browser thinks the page is somewhere else, so it will never find the stylesheet or other linked items.
Anyway, what I did to temporarily fix this whole situation was to create a redirect page, called 404Redirect.aspx, which I put in the web site root. It contains these lines:
<%@OutputCache Duration="604800" VaryByParam="None" %>
Then you configure IIS to redirect to this page on a 404. Once I did that, I end up on the SF page I created for the 404.
Unfortunately, I lose the original error page information because of the extra redirection. I'm going to either write an HttpHandler or else modify my 404Redirect.aspx to try to recover that information, as it's important to know. Of course, I can always look at the IIS logs for this, but I have a nice web tracking application that will give this information to the folks that are in charge of the content.
Ok, one more issue with the Sitefinity part.
If you ask for an ASPX page that doesn't exist, then SF handles it with a 302 then a 200 when it renders the PageNotFound.aspx page. This is wrong! It should return a 404 on the final page when it handles the error page from the web.config.
This is a big problem, because the page won't be removed from search engine indexes, and folks will continue to get this error page. So to hack a fix for this, until Telerik addresses the issue (or maybe there is a fix, and I don't know about it), you can create a separate master page template for error pages. Then you put some code in there that either looks to the http_referer header to find out where the user came from, in order to determine the actual error status code, or you create a new master page for each of the major error types. The object of the code is to write an HTTP status code. So, you might try a master page something like this:
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="ErrorPages.master.cs" Inherits="App_Master_ErrorPages" %>
<% Response.StatusCode = 404; %>
<rest of master page code here>
It's important to write the status code before you send any text to the browser!
A while back I wrote a nifty redirection handler for MCMS 2002. Whenever a page not found error occured, it would look up the old URL in a list that was maintained by the web content folks. Whenever they'd move stuff around on the site, they'd make an entry in this list, mapping the old URL to the New one. If they moved a whole subtree of the site, they would only have to enter the top folder that was moved. You'll see why in a moment.
The algorithm went like this:
1. It checked the entire URL first, looking for a match. If it found one, it redirected (with a 301 moved permanently) to the new page.
2. If it didn't find one, it recursively walked up the URL looking for matches at the folder level. If it found one, it looked up the new URL for the folder, and looked to see if that original page still exists in the new folder's subtree. If so, it issues a 301 and redirects.
3. If it didn't find the page in the new subtree, it issued a 302 and redirected to the folder default page that it just matched.
4. If it never finds one at all, it logs the problem and redirects to the Page Not Found page, which then issues a 404.
This has the advantage of retaining your page rank for inbound links to pages that have been moved on your site (the 301), but purges a search engine's index of pages that don't truly exist. Also, I noticed a drastic reduction in 404 errors, because a lot of the time the page was still there, but only moved.
Problem solved. Now I just have to write a module like this for Sitefinity! ;^)