diff --git a/src/Umbraco.Web/Mvc/NotFoundHandler.cs b/src/Umbraco.Web/Mvc/NotFoundHandler.cs
new file mode 100644
index 0000000000..1d73f9b5e6
--- /dev/null
+++ b/src/Umbraco.Web/Mvc/NotFoundHandler.cs
@@ -0,0 +1,17 @@
+using System.Web;
+
+namespace Umbraco.Web.Mvc
+{
+ public class NotFoundHandler : IHttpHandler
+ {
+ public void ProcessRequest(HttpContext context)
+ {
+ context.Response.StatusCode = 404;
+ }
+
+ public bool IsReusable
+ {
+ get { return true; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByIdRouteHandler.cs b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByIdRouteHandler.cs
new file mode 100644
index 0000000000..cd50705cc8
--- /dev/null
+++ b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByIdRouteHandler.cs
@@ -0,0 +1,25 @@
+using System.Web.Routing;
+using Umbraco.Core.Models;
+
+namespace Umbraco.Web.Mvc
+{
+ public abstract class UmbracoVirtualNodeByIdRouteHandler : UmbracoVirtualNodeRouteHandler
+ {
+ private readonly int _realNodeId;
+
+ protected UmbracoVirtualNodeByIdRouteHandler(int realNodeId)
+ {
+ _realNodeId = realNodeId;
+ }
+
+ protected sealed override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext)
+ {
+ var byId = umbracoContext.ContentCache.GetById(_realNodeId);
+ if (byId == null) return null;
+
+ return FindContent(requestContext, umbracoContext, byId);
+ }
+
+ protected abstract IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext, IPublishedContent baseContent);
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs
new file mode 100644
index 0000000000..f8cd1f6006
--- /dev/null
+++ b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs
@@ -0,0 +1,47 @@
+using System.Web;
+using System.Web.Mvc;
+using System.Web.Routing;
+using Umbraco.Core.Models;
+using Umbraco.Web.Models;
+using Umbraco.Web.Routing;
+
+namespace Umbraco.Web.Mvc
+{
+ public abstract class UmbracoVirtualNodeRouteHandler : IRouteHandler
+ {
+ public IHttpHandler GetHttpHandler(RequestContext requestContext)
+ {
+ var umbracoContext = UmbracoContext.Current;
+
+ var found = FindContent(requestContext, umbracoContext);
+ if (found == null) return new NotFoundHandler();
+
+ umbracoContext.PublishedContentRequest = new PublishedContentRequest(
+ umbracoContext.CleanedUmbracoUrl, umbracoContext.RoutingContext)
+ {
+ PublishedContent = found
+ };
+
+ //allows inheritors to change the pcr
+ PreparePublishedContentRequest(umbracoContext.PublishedContentRequest);
+
+ //create the render model
+ var renderModel = new RenderModel(umbracoContext.PublishedContentRequest.PublishedContent, umbracoContext.PublishedContentRequest.Culture);
+
+ //assigns the required tokens to the request
+ requestContext.RouteData.DataTokens.Add("umbraco", renderModel);
+ requestContext.RouteData.DataTokens.Add("umbraco-doc-request", umbracoContext.PublishedContentRequest);
+ requestContext.RouteData.DataTokens.Add("umbraco-context", umbracoContext);
+
+ umbracoContext.PublishedContentRequest.ConfigureRequest();
+
+ return new MvcHandler(requestContext);
+ }
+
+ protected abstract IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext);
+
+ protected virtual void PreparePublishedContentRequest(PublishedContentRequest publishedContentRequest)
+ {
+ }
+ }
+}
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index eab4fbcb60..bb5f1b8699 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -362,8 +362,10 @@
+
+
@@ -471,6 +473,7 @@
+