In normal WebForms scenario, any root-relative URLs (e.g. ~/folder/file.txt) inside CSS files such as:

.form { background-image: url(~/Content/Images/form_bg.gif); }

will automatically get resolved during runtime if I specify

<head runat="server">

In the referencing page.

However, that is no longer happening on an ASP.NET MVC Beta1 website.

Is there a way I could enable this functionality without resorting to hacks or CSS-loader file? Like maybe HttpModules or something?

Or am I not desigining my website correctly? What is supposed to be a good design?

Since original ASP.NET WebForms already has this feature, I'd prefer to utilize any existing functionality if possible. But I don't have much clue.

This web application will be deployed in several environments where the ~ root folder might not be obvious.

EDIT: I mean the url in the file's CONTENT not the file's url itself.

Was it helpful?


I would not bother with the auto-root-finding ~ character. I understand that you want the same solution to work where the root directory differs between deployments, but within the CSS document you shouldn't have any problems using relative paths. The paths in the CSS document (to the image URL in your example) will always be relative to the location of the CSS file regardless of the path of any page that loads that CSS file. So if your images are in ~/Content/Images and your stylesheets are in ~/Content/Stylesheets, you'll always be able to use background-image: url(../Images/form_bg.gif); and it will work regardless of the location of the page that loads the stylesheet.

Is there a reason this wouldn't work?


One trick I have used in the past, was to actually make my CSS file have a .ASPX extension, and set the ContentType property in the page signature:

<%@ Page Language="C#" ContentType="text/css" %>

body {
    margin: 0;
    padding: 0;
    background: #C32605 url(<%= ResolveUrl("~/Content/themes/base/images/BodyBackground.png") %>) repeat-x;
    font-family: Verdana, Arial, sans-serif;
    font-size: small;
    color: #d7f9ff;

This will ensure that the CSS file goes through the ASP.NET framework, and replaces the server side code with your relative path.

Here are some resources on implementing IHttpModule to intercept web requests to your app...

Write/adapt one to check for filetype (e.g. pseudocode: if (request ends with ".css") ...)

then use a regular expression to replace all instances of "~/" with System.Web.VirtualPathUtility.ToAbsolute("~/")

I don't know what this will do to performance, running every request through this kind of a filter, but you can probably fiddle with your web.config file and/or your MVC URL routes to funnel all .css requests through this kind of a filter while skipping past it for other files.

Come to think of it, you can probably achieve the same effect inside an ASP.NET MVC app by pointing all your CSS refrences at a special controller.action that performs this kind of preprocessing for you. i doubt that would be as performant as an IHttpModule though.

If you're trying to parse the ~/ out of any file, including text files, javascript, etc, you can write a handler that assigns a filter to it and you can use that to search for those paths... for example...

public class StringParsingFilter : MemoryStream {

    public Stream OriginalStream {
        get { return this.m_OriginalStream; }
        set { this.m_OriginalStream = value; }
    private System.IO.Stream m_OriginalStream;

    public StringParsingFilter() : base() {
        this.m_OriginalStream = null;

    public override void Flush() {

    public override void Write(byte[] buffer, int offset, int count) {

        //otherwise, parse for the correct content
        string value = System.Text.Encoding.Default.GetString(buffer);
        string contentType = HttpContext.Current.Response.ContentType;

        //Do any parsing here

        //write the new bytes to the stream
        byte[] bytes = System.Text.Encoding.Default.GetBytes(value);
        this.m_OriginalStream.Write(bytes, offset, count + (bytes.Length - buffer.Length));



And you'll write a custom handler to know when to assign this filter... like the following...

 public class FilterControlModule : IHttpModule {

    public void Init(HttpApplication context) {
        HttpApplication oAppContext = context;
        oAppContext.BeginRequest += new EventHandler(_HandleSettingFilter);                        

    private void _HandleSettingFilter(object sender, EventArgs e) {

        //You might check the file at this part to make sure
        //it is a file type you want to parse
        //if (!CurrentFile.isStyleSheet()) { return; }

        //assign the new filter
        StringParsingFilter filter = new StringParsingFilter();
        filter.OriginalStream = HttpContext.Current.Response.Filter;
        HttpContext.Current.Response.Filter = (Stream)filter;



It may have actually been easier just to say "look up IHttpModules" but this is some code that I've used to parse files for paths other than files.

You'll also have to change some things in your IIS settings to allow the files to be parsed by setting the ISAPI to be a wildcard for all of the files that get handled. You can see more at this website, if you're using IIS6 that is...

You can also use this to modify any file types so you could assign some filters for images, some for javascript or stylesheets or ... really anything...

You could use an URL Rewriter to fix the URL as the request comes in, though I am not so sure it is so much elegant as a hack in this case.

I created a PathHelper util class that gives me all the paths I need. For example

<link href="<%=PathHelper.CssUrl("FormulaIndex.css")%>" rel="Stylesheet" type="text/css"/>

Gives me the correct full url with the help of System.Web.VirtualPathUtility.ToAbsolute() and my own convention (content/css/yourFile.css).

I did the same for js, xml, t9n, pics... Its central, reusable and now I only had to change one line to catch the move of the scripts folder from content/js to Scripts in all my websites and pages.

A moronic move if you ask me, but it's reality in the current beta :(

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top