diff --git a/page_manager_ui/src/Form/PageParametersForm.php b/page_manager_ui/src/Form/PageParametersForm.php
index e1a9a81..791a9ee 100644
--- a/page_manager_ui/src/Form/PageParametersForm.php
+++ b/page_manager_ui/src/Form/PageParametersForm.php
@@ -48,18 +48,20 @@ class PageParametersForm extends FormBase {
         $this->t('Machine name'),
         $this->t('Label'),
         $this->t('Type'),
+        $this->t('Optional'),
         $this->t('Operations'),
       ],
-      '#rows' => $this->renderRows($cached_values),
+      '#rows' => $this->renderRows($cached_values, $form, $form_state),
       '#empty' => $this->t('There are no parameters defined for this page.')
     ];
     return $form;
   }
 
-  protected function renderRows($cached_values) {
+  protected function renderRows($cached_values, array &$form, FormStateInterface $form_state) {
     $rows = [];
-    /** @var $page \Drupal\page_manager\Entity\Page */
+    /** @var \Drupal\page_manager\Entity\Page $page */
     $page = $cached_values['page'];
+    $parameter_required = TRUE;
     /**
      * @var string $parameter
      */
@@ -74,6 +76,7 @@ class PageParametersForm extends FormBase {
         $row['type']['colspan'] = 2;
       }
       $row['type']['data'] = $parameter['type'] ?: $this->t('<em>No context assigned</em>');
+      $row['optional'] = $parameter['optional'] ? $this->t('Optional') : $this->t('Required');
 
       list($route_partial, $route_parameters) = $this->getOperationsRouteInfo($cached_values, $cached_values['id'], $parameter_name);
       $build = [
@@ -82,6 +85,12 @@ class PageParametersForm extends FormBase {
       ];
       $row['operations']['data'] = $build;
       $rows[$parameter_name] = $row;
+
+      // Validate parameter order.
+      if (!$parameter_required && !$parameter['optional']) {
+        drupal_set_message($this->t('You cannot put optional path parameters before required parameters.'), 'error');
+      }
+      $parameter_required = !$parameter['optional'];
     }
 
     return $rows;
diff --git a/page_manager_ui/src/Form/ParameterEditForm.php b/page_manager_ui/src/Form/ParameterEditForm.php
index fa9cbec..3866b7f 100644
--- a/page_manager_ui/src/Form/ParameterEditForm.php
+++ b/page_manager_ui/src/Form/ParameterEditForm.php
@@ -142,6 +142,12 @@ class ParameterEditForm extends FormBase {
       '#default_value' => $parameter['type'],
     ];
 
+    $form['optional'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Optional'),
+      '#default_value' => $parameter['optional'],
+    ];
+
     $form['actions'] = ['#type' => 'actions'];
     $form['actions']['submit'] = [
       '#type' => 'submit',
@@ -191,13 +197,14 @@ class ParameterEditForm extends FormBase {
     $page = $cache_values['page'];
     $name = $form_state->getValue('machine_name');
     $type = $form_state->getValue('type');
+    $optional = $form_state->getValue('optional');
     if ($type === static::NO_CONTEXT_KEY) {
       $page->removeParameter($name);
       $label = NULL;
     }
     else {
       $label = $form_state->getValue('label');
-      $page->setParameter($name, $type, $label);
+      $page->setParameter($name, $type, $label, $optional);
     }
 
     $this->setTempstore($cache_values);
diff --git a/src/Entity/Page.php b/src/Entity/Page.php
index 6782fc7..e2d9cc9 100644
--- a/src/Entity/Page.php
+++ b/src/Entity/Page.php
@@ -126,6 +126,7 @@ class Page extends ConfigEntityBase implements PageInterface {
    * - machine_name: Machine-readable context name.
    * - label: Human-readable context name.
    * - type: Context type.
+   * - optional: Whether the parameter is optional.
    *
    * @var array[]
    */
@@ -268,11 +269,12 @@ class Page extends ConfigEntityBase implements PageInterface {
   /**
    * {@inheritdoc}
    */
-  public function setParameter($name, $type, $label = '') {
+  public function setParameter($name, $type, $label = '', $optional = FALSE) {
     $this->parameters[$name] = [
       'machine_name' => $name,
       'type' => $type,
       'label' => $label,
+      'optional' => $optional,
     ];
     // Reset contexts when a parameter is added or changed.
     $this->contexts = [];
diff --git a/src/PageInterface.php b/src/PageInterface.php
index ae134b9..2a0df62 100644
--- a/src/PageInterface.php
+++ b/src/PageInterface.php
@@ -173,10 +173,12 @@ interface PageInterface extends ConfigEntityInterface, EntityWithPluginCollectio
    *   The parameter context type.
    * @param string $label
    *   (optional) The parameter context label.
+   * @param bool $optional
+   *   (optional) If the he parameter context is optional.
    *
    * @return $this
    */
-  public function setParameter($name, $type, $label = '');
+  public function setParameter($name, $type, $label = '', $optional = FALSE);
 
   /**
    * Removes a specific parameter context.
diff --git a/src/Routing/PageManagerRoutes.php b/src/Routing/PageManagerRoutes.php
index 7def0d8..1e0d83a 100644
--- a/src/Routing/PageManagerRoutes.php
+++ b/src/Routing/PageManagerRoutes.php
@@ -85,6 +85,7 @@ class PageManagerRoutes extends RouteSubscriberBase {
       foreach ($entity->getParameters() as $parameter_name => $parameter) {
         if (!empty($parameter['type'])) {
           $parameters[$parameter_name]['type'] = $parameter['type'];
+          $parameters[$parameter_name]['required'] = !$parameter['optional'];
         }
       }
 
@@ -113,6 +114,11 @@ class PageManagerRoutes extends RouteSubscriberBase {
             '_admin_route' => $entity->usesAdminTheme(),
           ]
         );
+        foreach ($parameters as $key => $parameter) {
+          if (isset($parameter['required']) && $parameter['required'] === FALSE) {
+            $route->addDefaults([$key => NULL]);
+          }
+        }
         $collection->add($route_name . '_' . $variant_id, $route);
       }
 
diff --git a/src/Routing/VariantRouteFilter.php b/src/Routing/VariantRouteFilter.php
index 0c271d8..0e2b22e 100644
--- a/src/Routing/VariantRouteFilter.php
+++ b/src/Routing/VariantRouteFilter.php
@@ -224,6 +224,10 @@ class VariantRouteFilter implements RouteFilterInterface {
     $attributes = $request->attributes->all();
     $attributes = NestedArray::mergeDeep($attributes, $raw_attributes);
 
+    $attributes = array_filter($attributes, function($attribute){
+      return !is_null($attribute);
+    });
+
     // Run the route enhancers on the raw attributes. This performs the same
     // functionality as \Symfony\Cmf\Component\Routing\DynamicRouter::match().
     foreach ($this->getRouteEnhancers() as $enhancer) {
