Story Details for articles

StyleBundle 403 Error - Solved!

kahanu
Author:
Version:
Views:
2118
Date Posted:
2/6/2013 4:13:35 PM
Date Updated:
2/6/2013 4:13:35 PM
Rating:
5/3 votes
Framework:
ASP.NET MVC 4
Platform:
Windows
Programming Language:
C#
Technologies:
.Net 4.5
Tags:
stylebundle, scriptbundle, 403 Forbidden, ASP.NET MVC, css, javascript, KendoUI
Demo site:
Home Page:
Share:
This article will attempt to explain the reasons for the 403 Forbidden error when using the StyleBundles or ScriptBundles in an ASP.NET MVC 4 application in build mode, and explain how to solve the issues.

In this article, I'll show you how to build a site so that it works in Debug or Release mode using the StyleBundles and ScriptBundles.  I'll demonstrate these techniques with this website template.

sample web site

The Virtual Path Matters!

Apart from what you've probably heard and read in various Forums or Stackoverflow posts regarding the way to work with the StyleBundle and ScriptBundle classes, the "virtualpath" parameter of the classes DOES matter, as far as how it is named.  Some answerers have gotten this right.

Let me clarify this, if you have ALL of your CSS files in the root of the Content folder, then no, it doesn't matter.  It only matters if you have various folders inside the Content folder that can contain many different sets of styles for various reasons, and you want to maintain that folder structure.

I'll show you how to keep those folders without having to drop everything down to the root.

What you need to do is match the bundle name with the actual virtual path to your files!

I'll explain this in a moment, but let me show you what the framework does with the bundles from Debug mode to Release mode.

Let's say I have a Content folder with my CSS files and images in a structure like this:

content folder

This is how I want to keep my files so they are separate from each other. 

So let's say I've installed the halcyonic HTML into the _Layout.cshtml page.  And I've then created a StyleBundle like this:

bundles.Add(new StyleBundle("~/Content/halcyonic").Include(
    "~/Content/halcyonic/css/style.css",
    "~/Content/halcyonic/css/5grid/responsive.css"));

... this will work fine in Debug mode and return a path as:

http://localhost:32290/Content/halcyonic/css/images/someimage.png

But if I were to run this in Release mode, by changing the "debug" attribute on the compilation element in the web.config file to false...

<compilation debug="false" targetFramework="4.5" />

... and then run the application, I would get dreaded 403 Forbidden error and my page layout would break.  And you can clearly see below that my page doesn't have any styles attached.

broken site

And this is what the path to the same image file would look like in Release mode:

http://localhost:32290/Content/images/someimage.png

Obviously we can see that the path changed when changing to Release mode, but what happened?  I fuddled over this for a long time and was determined to figure it out and finally found a pattern.

If I changed the parameter name of the bundle to something that more closely matched to the actual path to the files, it started working.  So if you look at the picture of the Content folder and it's file structure again, you can see that the CSS file, style.css, lives in the /Content/halcyonic/css folder.

So I tried:

bundles.Add(new StyleBundle("~/Content/halcyonic/css").Include(
                "~/Content/halcyonic/css/style.css",
                "~/Content/halcyonic/css/5grid/responsive.css"));

Of course, each time I made a change here I had to update the Render method in the _Layout.cshtml page.

@Styles.Render("~/Content/halcyonic/css")

... and this too didn't work.  I got the same 403 Forbidden error.

So then I found out that the last part of the virtualPath is essentially a throw away name.  It doesn't really mean anything or have any other impact other than telling the framework that the information before it is important.  I'll tell you why in a second.

So then I tried this:

bundles.Add(new StyleBundle("~/Content/halcyonic/css/styles").Include(
                "~/Content/halcyonic/css/style.css",
                "~/Content/halcyonic/css/5grid/responsive.css"));

@Styles.Render("~/Content/halcyonic/css/styles")

... and it worked!

fixed site

Why does this work?

Let's take a closer look at why this now works.

bundles.Add(new StyleBundle("~/Content/halcyonic/css/styles").Include(
                 "~/Content/halcyonic/css/style.css",
                 "~/Content/halcyonic/css/5grid/responsive.css"));

I've lined up the StyleBundle parameter with the included files paths to show you how they correlate with each other.  Notice that they all match up to the last slash before the style sheet.  This is the important thing to remember.  Make the bundle parameter match the path up to the location of the style sheet, and then add a name, any name after that.

This will work:

bundles.Add(new StyleBundle("~/Content/halcyonic/css/foo").Include(
                 "~/Content/halcyonic/css/style.css",
                 "~/Content/halcyonic/css/5grid/responsive.css"));

@Styles.Render("~/Content/halcyonic/css/foo")

foo virtual path

It doesn't matter what the trailing name is. The framework will ignore it, but you need it to tell the framework that everything before it will be used as the virtualPath in Release mode.  If /css was the trailing name, then it would say that it will look for the files in /Content/halcyonic, which is wrong.

And before you say, "Well why can't I put just a trailing slash after css, as in /Content/halcyonic/css/?" (notice the trailing slash).  That also won't work.  While the CSS will render Ok, some images may break, so put in a name with no trailing slash.

So end the virtualPath parameter with a name without a trailing slash.

Using this method will also allow you to use the relative image paths in the CSS file.  One thing I've found that always works the best is to have the images folder as a child underneath the style sheet.

For KendoUI Users

To allow your KendoUI widgets to work in Release mode and Debug mode, here are the bundles and Render methods:

bundles.Add(new ScriptBundle("~/bundles/kendo/2012.3.1401/kendoscripts").Include(
        "~/Scripts/kendo/2012.3.1401/kendo.web.*", // or kendo.all.*
        "~/Scripts/kendo/2012.3.1401/kendo.aspnetmvc.*"));
 
// The Kendo CSS bundle
bundles.Add(new StyleBundle("~/Content/kendo/2012.3.1401/kendostyles").Include(
        "~/Content/kendo/2012.3.1401/kendo.common.*",
        "~/Content/kendo/2012.3.1401/kendo.default.*"));

@Styles.Render("~/Content/kendo/2012.3.1401/kendostyles")
@Scripts.Render("~/bundles/jquery","~/bundles/kendo/2012.3.1401/kendoscripts")

You would just change the version folder name to yours.

Conclusion


So that's essentially it!  And just to be clear, this same procedure would be used for the ScriptBundle classes as well.

I know there's a lot here, but hopefully it's pretty straight forward and understandable.  This should prevent you from having any more issues with getting the the CSS and Scripts to render properly in Release mode.

Have fun!

Comments

  • daniel.sirz@gmail.com
    Posted By: Daniel
    On: 5/17/2013 1:03:18 AM

    Hi kahanu,

    it's early in the morning and this article "made my day"! Thanks for sharing!

    Daniel

  • info@kingwilder.com
    Posted By: King Wilder
    On: 5/17/2013 11:45:15 AM
    @Daniel - I'm glad it helped.
  • dkellycollins@gmail.com
    Posted By: Devin
    On: 5/30/2013 11:46:33 AM
    Had this error while in debug mode! Thanks for the help.
  • info@kingwilder.com
    Posted By: King Wilder
    On: 5/30/2013 12:06:21 PM
    @Devin - I'm glad the article helped.
  • drewid@drewspring.com
    Posted By: drevange
    On: 6/5/2013 3:28:39 AM
    Thanks!  Saved the day.  Tremendously useful and well document article.
  • info@kingwilder.com
    Posted By: King Wilder
    On: 6/5/2013 11:33:33 AM
    @drevange - thanks for the kind words, I'm glad it helped you out.
  • leniel@gmail.com
    Posted By: Leniel Macaferi
    On: 6/11/2013 6:18:19 PM

    Hey King Wilder,

    You're the same guy that provided the awesome SecurityGuard... https://github.com/kahanu/Security-Guard

    While searching for 403 forbidden related to style bundling I found you once again.

    Hooray... now I got it forever and ever!

    Using this method will also allow you to use the relative image paths in the CSS file.

    This is the biggest win.

    Thank you very much for the detailed post.

    Best,

    Leniel
    http://leniel.net

  • info@kingwilder.com
    Posted By: King Wilder
    On: 6/11/2013 6:32:27 PM
    @Leniel - Hi, how are things? I'm glad you found this solution helpful.  It was something I was determined to figure out and pass on to the community, if I got it right.  :^)

 

User Name:
(Required)
Email:
(Required)