More on URL Rebasing in Master Pages

Posted on April 23, 2006  |  

Posted in Development


Master pages are strange beasts. Scott Guthrie hinted earlier that there was no need for my BaseURL property to assist with proper linking to external CSS and JavaScript files. The runtime does its magic by adjusting URLs in the page head. However, it appears, the magic misfires at times.

A sample project

Suppose we have a simple project with two style sheets and a single web form. My master page has the following markup in its <head>:

<style type="text/css">
   @import '../css/screen.css';
<link type="text/css" rel="stylesheet" href="../css/print.css"/>

Note: the path to both stylesheets is relative to the master page location.

When I run default.aspx, the screen.css stylesheet isn’t applied. What’s wrong with the rendered markup:

<style type="text/css">
    @import '../css/screen.css';
<link type="text/css" rel="stylesheet" href="css/print.css" />

You see that the path to print.css got properly rebased (“adjusted”, if you will), but the <style> section was left untouched. My guess is the runtime knows how to parse only primitives like <link>.

There are two workarounds for this. You can use a property similar to my BaseURL or, as Dave Griffiths suggested, apply ResolveUrl:

<style type="text/css">
   @import '<%= ResolveUrl("~/css/screen.css") %>';
   @import '<%= BaseURL %>/css/screen.css';

The difference between the two is seen in the rendered markup:

<style type="text/css">
    @import '/UrlRebasing/css/screen.css';
    @import 'http://localhost:1100/UrlRebasing/css/screen.css';

It’s up to you to choose relative or absolute URLs.

Wait, There’s More!

Here’s a very interesting quirk. Would rebasing take place in this declaration?

<link type="text/css" rel="stylesheet"
      href='<%= ResolveUrl("~/css/print.css") %>'/>

Yes, but the output is incorrect:

<link type="text/css" rel="stylesheet" 
%20%25%3E" />

Apparently, the parser takes the <%= %> construct too literally.


I’d like to emphasize that this discussion is limited to CSS and JavaScript declarations in the <head> tag of a master page. Outside of <head> no magic rebasing happens, so you’re on your own. Use ResolveUrl or BaseURL for markup inside the <body>.


Shane Shepherd
on April 23, 2006

Why not just use Request.ApplicationPath?

Milan Negovan
on April 23, 2006

ResolveClientUrl seems to be a good fit too. There are so many ways to skin this cat. :)

Kent Boogaart
on April 25, 2006

You can also just plonk your css into your theme folder. Then ASP.NET will automatically add links to your head (assuming it has runat="server").


on August 8, 2007

ASP 2.0 doesn't do url rebasing for script tags.

on May 26, 2008

I have been bashing away for 6 hours now on broken image links inside nested master pages. I have read this blog and scotts blog and Im having no luck at all. I have tried everything to get the links working:
I changed the image tags to server side tags. I moved the css file and master page file into the same directory as the web page. I tried the ResolveClientURL .... nothing works .. and yet if i view the rendered source, all the image links are correct...


Nathanael Jones
on August 9, 2008

HtmlHead does the parsing for meta and link. It can't parse inside CPH tags.

It's easy to solve this problem at the application level, by sub-classing Page (which you should anyway).

This patch makes script, meta, and link tags work correctly regardless of how they land in the page header. It takes a quick second pass to clean up after ASP.NET.

You can also play with the acid test which demonstrates the incorrect behavior of the framework, and the correct behavior with the patch

on March 3, 2009

>Apparently, the parser takes the construct too literally.

In my masterpage single quotes don't work but double quotes work fine.

Broken in masterpage:

Works in masterpage:

Even odder is that if I put exactly the same thing in a content page placeHolder that is in the masterpage head section, the single quotes works fine.

Also at least in my masterpage with the head tag having a runat=server, it resolves ~/ without my doing anything, no need to add a runat=server to the link tag.

Works in my masterpage:

on November 5, 2009

Well, ASP.NET won't render any inline tags if used in "server side evaluated" elements (ASP.NET 3.5 SP1) that provides the runat attribute. Even if there's no runat attribute in the link element it is always being "run at server" - that's why the path is always being rebased inside the link element.