diff -u b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
--- b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
+++ b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
@@ -36,71 +36,36 @@
 
   /**
-   * Tries to upcast every variable to an entity type.
+   * Implements \Drupal\Core\ParamConverter\ParamConverterInterface::convert().
    *
-   * If there is a type denoted in the route options it will try to upcast to
-   * it, if there is no definition in the options it will try to upcast to an
-   * entity type of that name. If the chosen enity type does not exists it will
-   * leave the variable untouched.
-   * If the entity type exist, but there is no entity with the given id it will
-   * convert the variable to NULL.
-   *
-   * Example:
-   *
-   * pattern: '/a/{user}/some/{foo}/and/{bar}/'
-   * options:
-   *   converters:
-   *     foo: 'node'
-   *
-   * The value for {user} will be converted to a user entity and the value
-   * for {foo} to a node entity, but it will not touch the value for {bar}.
-   *
-   * It will not process variables which are marked as converted. It will mark
-   * any variable it processes as converted.
-   *
-   * @param array &$variables
-   *   Array of values to convert to their corresponding objects, if applicable.
-   * @param \Symfony\Component\Routing\Route $route
-   *   The route object.
-   * @param array &$converted
-   *   Array collecting the names of all variables which have been
-   *   altered by a converter.
+   * For every parameter whose desired type is an entity type, convert it to
+   * the loaded entity. Leave other parameters untouched.
    */
-  public function process(array &$variables, Route $route, array &$converted) {
-    $variable_names = $route->compile()->getVariables();
-
-    $options = $route->getOptions();
-    $configuredTypes = isset($options['converters']) ? $options['converters'] : array();
-
+  public function convert($unconverted_parameters, $parameter_options, Route $route, array $defaults) {
     $entityTypes = array_keys($this->entityManager->getDefinitions());
 
-    foreach ($variable_names as $name) {
-      // Do not process this variable if it's already marked as converted.
-      if (in_array($name, $converted)) {
-        continue;
-      }
-
-      // Obtain entity type to convert to from the route configuration or just
-      // use the variable name as default.
-      if (array_key_exists($name, $configuredTypes)) {
-        $type = $configuredTypes[$name];
-      }
-      else {
-        $type = $name;
+    $converted = array();
+    foreach ($unconverted_parameters as $name => $value) {
+      if (isset($parameter_options[$name]['type'])) {
+        $type = $parameter_options[$name]['type'];
+        if (in_array($type, $entityTypes)) {
+          $converted[$name] = $this->loadEntity($type, $value);
+        }
       }
+    }
 
-      if (in_array($type, $entityTypes)) {
-        $value = $variables[$name];
-
-        $storageController = $this->entityManager->getStorageController($type);
-        $entities = $storageController->load(array($value));
-
-        // Make sure $entities is null, if upcasting fails.
-        $entity = $entities ? reset($entities) : NULL;
-        $variables[$name] = $entity;
+    return $converted;
+  }
 
-        // Mark this variable as converted.
-        $converted[] = $name;
-      }
-    }
+  /**
+   * Loads a single entity by id.
+   *
+   * @todo Remove when EntityManager provides a direct API for loading a single
+   *   entity: http://drupal.org/node/1867228.
+   */
+  protected function loadEntity($type, $id) {
+    $storageController = $this->entityManager->getStorageController($type);
+    $entities = $storageController->load(array($id));
+    $entity = $entities ? reset($entities) : NULL;
+    return $entity;
   }
 }
diff -u b/core/lib/Drupal/Core/ParamConverter/ParamConverterInterface.php b/core/lib/Drupal/Core/ParamConverter/ParamConverterInterface.php
--- b/core/lib/Drupal/Core/ParamConverter/ParamConverterInterface.php
+++ b/core/lib/Drupal/Core/ParamConverter/ParamConverterInterface.php
@@ -19,11 +19,22 @@
    *
-   * @param array &$variables
+   * @param array $unconverted_parameters
    *   Array of values to convert to their corresponding objects, if applicable.
+   *   This array excludes parameters that have already been converted by prior
+   *   converters.
+   * @param array $parameter_options
+   *   An array of options for each parameter, as specified by the route's
+   *   'parameters' option, and altered by the param converter manager.
    * @param \Symfony\Component\Routing\Route $route
    *   The route object.
-   * @param array &$converted
-   *   Array collecting the names of all variables which have been
-   *   altered by a converter.
+   * @param array $defaults
+   *   The complete array of parameters returned by
+   *   \Symfony\Component\Routing\Matcher\RequestMatcherInterface::matchRequest()
+   *   and processed by prior route enhancers and parameter converters.
+   *
+   * @return array
+   *   An array of converted parameters. Omitted parameters will be treated as
+   *   still unconverted and forwarded to the following converter. Parameters
+   *   set to NULL will result in a 404 response.
    */
-  public function process(array &$variables, Route $route, array &$converted);
+  public function convert($unconverted_parameters, $parameter_options, Route $route, array $defaults);
 }
diff -u b/core/lib/Drupal/Core/ParamConverter/ParamConverterManager.php b/core/lib/Drupal/Core/ParamConverter/ParamConverterManager.php
--- b/core/lib/Drupal/Core/ParamConverter/ParamConverterManager.php
+++ b/core/lib/Drupal/Core/ParamConverter/ParamConverterManager.php
@@ -61,27 +61,31 @@
    *   The modified defaults.
    */
   public function enhance(array $defaults, Request $request) {
-    // This array will collect the names of all variables which have been
-    // altered by a converter.
-    // This serves two purposes:
-    // 1. It might prevent converters later in the pipeline to process
-    //    a variable again.
-    // 2. To check if upcasting was successfull after each converter had
-    //    a go. See below.
-    $converters = array();
-
     $route = $defaults[RouteObjectInterface::ROUTE_OBJECT];
+    $options = $route->getOptions();
+    $parameter_options = isset($options['parameters']) ? $options['parameters'] : array();
 
+    // For each parameter without explicit options, default its type to the
+    // parameter name.
+    foreach ($route->compile()->getVariables() as $parameter_name) {
+      if (!isset($parameter_options[$parameter_name])) {
+        $parameter_options[$parameter_name] = array('type' => $parameter_name);
+      }
+    }
+
+    // Invoke each registered converter.
+    $unconverted_parameters = $defaults;
     foreach ($this->converters as $converter) {
-      $converter->process($defaults, $route, $converters);
+      $converted_parameters = $converter->convert($unconverted_parameters, $parameter_options, $route, $defaults);
+      $defaults = $converted_parameters + $defaults;
+      $unconverted_parameters = array_diff_key($unconverted_parameters, $converted_parameters);
     }
 
-    // Check if all upcasting yielded a result.
-    // If an upcast value is NULL do a 404.
-    foreach ($converters as $variable) {
-      if ($defaults[$variable] === NULL) {
-        throw new NotFoundHttpException();
-      }
+    // A parameter explicitly converted to NULL means the requested resource
+    // wasn't found.
+    $converted_parameters = array_diff_key($defaults, $unconverted_parameters);
+    if (in_array(NULL, $converted_parameters)) {
+      throw new NotFoundHttpException();
     }
 
     return $defaults;
diff -u b/core/modules/system/lib/Drupal/system/Tests/ParamConverter/UpcastingTest.php b/core/modules/system/lib/Drupal/system/Tests/ParamConverter/UpcastingTest.php
--- b/core/modules/system/lib/Drupal/system/Tests/ParamConverter/UpcastingTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/ParamConverter/UpcastingTest.php
@@ -50,14 +50,12 @@
     $this->assertRaw("user: {$user->label()}, node: {$node->label()}, foo: $foo", 'user and node upcast by entity name');
 
     // paramconverter_test/test_node_user_user/{node}/{foo}/{user}
-    // converters:
-    //   foo: 'user'
+    // options.parameters.foo.type = user
     $this->drupalGet("paramconverter_test/test_node_user_user/{$node->nid}/{$user->uid}/{$user->uid}");
     $this->assertRaw("user: {$user->label()}, node: {$node->label()}, foo: {$user->label()}", 'foo converted to user as well');
 
     // paramconverter_test/test_node_node_foo/{user}/{node}/{foo}
-    // converters:
-    //   user: 'node'
+    // options.parameters.user.type = node
     $this->drupalGet("paramconverter_test/test_node_node_foo/{$node->nid}/{$node->nid}/$foo");
     $this->assertRaw("user: {$node->label()}, node: {$node->label()}, foo: $foo", 'user is upcast to node (rather than to user)');
   }
@@ -69,8 +67,7 @@
     $node = $this->drupalCreateNode(array('title' => $this->randomName(8)));
     $parent = $this->drupalCreateNode(array('title' => $this->randomName(8)));
     // paramconverter_test/node/{node}/set/parent/{parent}
-    // converters:
-    //   parent: 'node'
+    // options.parameters.parent.type = node
     $this->drupalGet("paramconverter_test/node/" . $node->nid . "/set/parent/" . $parent->nid);
     $this->assertRaw("Setting '" . $parent->title . "' as parent of '" . $node->title . "'.");
   }
diff -u b/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.routing.yml b/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.routing.yml
--- b/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.routing.yml
+++ b/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.routing.yml
@@ -12,8 +12,9 @@
   requirements:
     _access: 'TRUE'
   options:
-    converters:
-      foo: 'user'
+    parameters:
+      foo:
+        type: 'user'
 
 paramconverter_test_node_node_foo:
   pattern: '/paramconverter_test/test_node_node_foo/{user}/{node}/{foo}'
@@ -22,8 +23,9 @@
   requirements:
     _access: 'TRUE'
   options:
-    converters:
-      user: 'node'
+    parameters:
+      user:
+        type: 'node'
 
 paramconverter_test_node_set_parent:
   pattern: '/paramconverter_test/node/{node}/set/parent/{parent}'
@@ -35,2 +37,3 @@
-    converters:
-      parent: 'node'
+    parameters:
+      parent:
+        type: 'node'
