diff --git a/core/routes.php b/core/routes.php
index 76cf03c36733404d6b2d32c7a4c0db559ad0047b..74be88061200befdd596a7d71e2ad1ae201197c1 100644
--- a/core/routes.php
+++ b/core/routes.php
@@ -7,7 +7,8 @@
  */
 
 // Post installation check
-/** @var $this OC_Router */
+
+/** @var $this OCP\Route\IRouter */
 $this->create('post_setup_check', '/post-setup-check')
 	->action('OC_Setup', 'postSetupCheck');
 
diff --git a/lib/private/route/cachingrouter.php b/lib/private/route/cachingrouter.php
new file mode 100644
index 0000000000000000000000000000000000000000..ad25372391f5a882b127a5bd7c8c0f2fe9fedf80
--- /dev/null
+++ b/lib/private/route/cachingrouter.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Route;
+
+class CachingRouter extends Router {
+	/**
+	 * @var \OCP\ICache
+	 */
+	protected $cache;
+
+	/**
+	 * @param \OCP\ICache $cache
+	 */
+	public function __construct($cache) {
+		$this->cache = $cache;
+		parent::__construct();
+	}
+
+	/**
+	 * Generate url based on $name and $parameters
+	 *
+	 * @param string $name Name of the route to use.
+	 * @param array $parameters Parameters for the route
+	 * @param bool $absolute
+	 * @return string
+	 */
+	public function generate($name, $parameters = array(), $absolute = false) {
+		$key = $name . json_encode($parameters) . $absolute;
+		if ($this->cache->hasKey($key)) {
+			return $this->cache->get($key);
+		} else {
+			$url = parent::generate($name, $parameters, $absolute);
+			$this->cache->set($key, $url, 3600);
+			return $url;
+		}
+	}
+}
diff --git a/lib/private/route/router.php b/lib/private/route/router.php
index 60ba587840157fda6470df7b2016c947ddd5240a..bad74c925fae763fb8306a7e5dad8b1edeb94a98 100644
--- a/lib/private/route/router.php
+++ b/lib/private/route/router.php
@@ -47,6 +47,8 @@ class Router implements IRouter {
 
 	protected $loaded = false;
 
+	protected $loadedApps = array();
+
 	public function __construct() {
 		$baseUrl = \OC_Helper::linkTo('', 'index.php');
 		if (!\OC::$CLI) {
@@ -93,27 +95,46 @@ class Router implements IRouter {
 	/**
 	 * loads the api routes
 	 */
-	public function loadRoutes() {
+	public function loadRoutes($app = null) {
 		if ($this->loaded) {
 			return;
 		}
-		$this->loaded = true;
-		foreach ($this->getRoutingFiles() as $app => $file) {
-			$this->useCollection($app);
-			require_once $file;
-			$collection = $this->getCollection($app);
-			$collection->addPrefix('/apps/' . $app);
+		if (is_null($app)) {
+			$this->loaded = true;
+			$routingFiles = $this->getRoutingFiles();
+		} else {
+			if (isset($this->loadedApps[$app])) {
+				return;
+			}
+			$file = \OC_App::getAppPath($app) . '/appinfo/routes.php';
+			if (file_exists($file)) {
+				$routingFiles = array($app => $file);
+			} else {
+				$routingFiles = array();
+			}
+		}
+		foreach ($routingFiles as $app => $file) {
+			if (!$this->loadedApps[$app]) {
+				$this->loadedApps[$app] = true;
+				$this->useCollection($app);
+				require_once $file;
+				$collection = $this->getCollection($app);
+				$collection->addPrefix('/apps/' . $app);
+				$this->root->addCollection($collection);
+			}
+		}
+		if (!isset($this->loadedApps['core'])) {
+			$this->loadedApps['core'] = true;
+			$this->useCollection('root');
+			require_once 'settings/routes.php';
+			require_once 'core/routes.php';
+
+			// include ocs routes
+			require_once 'ocs/routes.php';
+			$collection = $this->getCollection('ocs');
+			$collection->addPrefix('/ocs');
 			$this->root->addCollection($collection);
 		}
-		$this->useCollection('root');
-		require_once 'settings/routes.php';
-		require_once 'core/routes.php';
-
-		// include ocs routes
-		require_once 'ocs/routes.php';
-		$collection = $this->getCollection('ocs');
-		$collection->addPrefix('/ocs');
-		$this->root->addCollection($collection);
 	}
 
 	/**
@@ -152,12 +173,21 @@ class Router implements IRouter {
 	}
 
 	/**
-	 * Find the route matching $url.
+	 * Find the route matching $url
 	 *
 	 * @param string $url The url to find
 	 * @throws \Exception
 	 */
 	public function match($url) {
+		if (substr($url, 0, 6) === '/apps/') {
+			// empty string / 'apps' / $app / rest of the route
+			list(, , $app,) = explode('/', $url, 4);
+			$this->loadRoutes($app);
+		} else if (substr($url, 0, 6) === '/core/' or substr($url, 0, 5) === '/ocs/' or substr($url, 0, 10) === '/settings/') {
+			$this->loadRoutes('core');
+		} else {
+			$this->loadRoutes();
+		}
 		$matcher = new UrlMatcher($this->root, $this->context);
 		$parameters = $matcher->match($url);
 		if (isset($parameters['action'])) {
@@ -196,6 +226,7 @@ class Router implements IRouter {
 	 * @return string
 	 */
 	public function generate($name, $parameters = array(), $absolute = false) {
+		$this->loadRoutes();
 		return $this->getGenerator()->generate($name, $parameters, $absolute);
 	}
 
diff --git a/lib/private/server.php b/lib/private/server.php
index 8c9ea39c5627b083585e616bc84d631de0a77b6e..5c83f3ef495acb49b90d5bb8fc8061bbf423de42 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -159,7 +159,15 @@ class Server extends SimpleContainer implements IServerContainer {
 			return new \OC\BackgroundJob\JobList($c->getDatabaseConnection(), $config);
 		});
 		$this->registerService('Router', function ($c){
-			$router = new \OC\Route\Router();
+			/**
+			 * @var Server $c
+			 */
+			$cacheFactory = $c->getMemCacheFactory();
+			if ($cacheFactory->isAvailable()) {
+				$router = new \OC\Route\CachingRouter($cacheFactory->create('route'));
+			} else {
+				$router = new \OC\Route\Router();
+			}
 			return $router;
 		});
 	}
@@ -327,7 +335,7 @@ class Server extends SimpleContainer implements IServerContainer {
 	/**
 	 * Returns an \OCP\CacheFactory instance
 	 *
-	 * @return \OCP\CacheFactory
+	 * @return \OCP\ICacheFactory
 	 */
 	function getMemCacheFactory() {
 		return $this->query('MemCacheFactory');
@@ -375,8 +383,6 @@ class Server extends SimpleContainer implements IServerContainer {
 	 * @return \OCP\Route\IRouter
 	 */
 	function getRouter(){
-		$router = $this->query('Router');
-		$router->loadRoutes();
-		return $router;
+		return $this->query('Router');
 	}
 }
diff --git a/lib/public/route/irouter.php b/lib/public/route/irouter.php
index d6b0750ba6f0b60c6f75dcbb61bf933006b258e8..125cd29e81b582519f217a8edfca25595fd26b55 100644
--- a/lib/public/route/irouter.php
+++ b/lib/public/route/irouter.php
@@ -10,8 +10,6 @@ namespace OCP\Route;
 
 interface IRouter {
 
-	public function __construct();
-
 	/**
 	 * Get the files to load the routes from
 	 *
@@ -24,7 +22,7 @@ interface IRouter {
 	/**
 	 * loads the api routes
 	 */
-	public function loadRoutes();
+	public function loadRoutes($app = null);
 
 	/**
 	 * Sets the collection to use for adding routes
diff --git a/settings/routes.php b/settings/routes.php
index 6954bd3823d68573c8249a3b096ea110c8301dad..a8bb0d981e8a0ede6c5ff61ce331265d5ea8799e 100644
--- a/settings/routes.php
+++ b/settings/routes.php
@@ -6,7 +6,7 @@
  * See the COPYING-README file.
  */
 
-/** @var $this OC_Router */
+/** @var $this OCP\Route\IRouter */
 
 // Settings pages
 $this->create('settings_help', '/settings/help')