diff --git a/rename_admin_paths.services.yml b/rename_admin_paths.services.yml
index a8ad2c5..85f1db2 100644
--- a/rename_admin_paths.services.yml
+++ b/rename_admin_paths.services.yml
@@ -1,6 +1,7 @@
 services:
-  rename_admin_paths.rename_admin_paths_processor:
-    class: Drupal\rename_admin_paths\RenameAdminPathsProcessor
-    tags:
-      - { name: path_processor_inbound, priority: 290 }
-      - { name: path_processor_outbound, priority: 100 }
+  rename_admin_paths.rename_admin_paths_event_subscriber:
+      class: 'Drupal\rename_admin_paths\EventSubscriber\RenameAdminPathEventSubscriber'
+      arguments:
+          - '@config.factory'
+      tags:
+          - { name: event_subscriber }
diff --git a/src/EventSubscriber/RenameAdminPathEventSubscriber.php b/src/EventSubscriber/RenameAdminPathEventSubscriber.php
new file mode 100644
index 0000000..eb9f560
--- /dev/null
+++ b/src/EventSubscriber/RenameAdminPathEventSubscriber.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\rename_admin_paths\EventSubscriber;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\Core\Routing\RoutingEvents;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+class RenameAdminPathEventSubscriber implements EventSubscriberInterface {
+
+  /**
+   * @var \Drupal\Core\Config\Config|\Drupal\Core\Config\ImmutableConfig
+   */
+  private $config;
+
+  /**
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   */
+  public function __construct(ConfigFactoryInterface $config_factory) {
+    $this->config = $config_factory->get('rename_admin_paths.settings');
+  }
+
+  /**
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   */
+  public function onRoutesAlter(RouteBuildEvent $event) {
+    foreach (['admin', 'user'] as $path_name) {
+      if ($this->config->get(sprintf('%s_path', $path_name))) {
+        $this->replaceRouteCollection($event->getRouteCollection(), $path_name, $this->config->get(sprintf('%s_path_value', $path_name)));
+      }
+    }
+  }
+
+  /**
+   * @param \Symfony\Component\Routing\RouteCollection $route_collection
+   * @param string $from
+   * @param string $to
+   */
+  private function replaceRouteCollection(RouteCollection $route_collection, $from, $to) {
+    foreach ($route_collection as $route) {
+      $this->replacePath($route, $from, $to);
+    }
+  }
+
+  /**
+   * @param \Symfony\Component\Routing\Route $route
+   * @param string $from
+   * @param string $to
+   */
+  private function replacePath(Route $route, $from, $to) {
+    if ($this->matchRoute($route, $from)) {
+      $route->setPath(preg_replace(sprintf('~^/%s~', $from), sprintf('/%s', $to), $route->getPath(), 1));
+    }
+  }
+
+  /**
+   * @param \Symfony\Component\Routing\Route $route
+   * @param string $path
+   *
+   * @return boolean
+   *
+   * match /path, /path/ and /path/* but not /path*
+   */
+  private function matchRoute(Route $route, $path)
+  {
+    return (bool) preg_match(sprintf('~^/%s(?:/(.*))?$~', $path), $route->getPath());
+  }
+
+  /**
+   * @return array
+   */
+  public static function getSubscribedEvents() {
+    return [
+      RoutingEvents::ALTER => [
+        ['onRoutesAlter', -2048],
+      ],
+    ];
+  }
+}
diff --git a/src/Form/RenameAdminPathsCallbacks.php b/src/Form/RenameAdminPathsCallbacks.php
index 923a1ed..5d8a818 100644
--- a/src/Form/RenameAdminPathsCallbacks.php
+++ b/src/Form/RenameAdminPathsCallbacks.php
@@ -3,25 +3,34 @@
 namespace Drupal\rename_admin_paths\Form;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
 
-/**
- * RenameAdminPathsCallbacks class.
- */
 class RenameAdminPathsCallbacks {
 
+  use StringTranslationTrait;
+
   /**
    * Form element validation handler for 'name' in form_test_validate_form().
    *
-   * @param string $element
+   * @param $element
    *   The field element.
    * @param \Drupal\Core\Form\FormStateInterface $form_state
    *   The form state object.
    */
   public function validatePath(&$element, FormStateInterface $form_state) {
-    // Force path replacement values to contain only lowercase letters, numbers and underscores.
-    if (!empty($element['#value']) && !preg_match('!^[a-z0-9_]+$!i', $element['#value'])) {
-      $form_state->setError($element, t('Path replacement value must contain only lowercase letters, numbers and underscores.'));
+    // Force path replacement values to contain only lowercase letters, numbers, and underscores.
+    if (!empty($element['#value']) && !$this->isValidPath($element['#value'])) {
+      $form_state->setError($element, $this->t('Path replacement value must contain only letters, numbers, hyphens and underscores.'));
     }
   }
 
+  /**
+   * @param string $value
+   *
+   * @return boolean
+   */
+  private function isValidPath($value)
+  {
+    return (bool) preg_match('~^[a-zA-Z0-9_-]+$~', $value);
+  }
 }
diff --git a/src/Form/RenameAdminPathsSettingsForm.php b/src/Form/RenameAdminPathsSettingsForm.php
index f441afc..abaed82 100644
--- a/src/Form/RenameAdminPathsSettingsForm.php
+++ b/src/Form/RenameAdminPathsSettingsForm.php
@@ -2,16 +2,21 @@
 
 namespace Drupal\rename_admin_paths\Form;
 
+use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Form\ConfigFormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Routing\RouteBuilderInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
-/**
- * Implements an example form.
- */
 class RenameAdminPathsSettingsForm extends ConfigFormBase {
 
   /**
-   * {@inheritdoc}
+   * @var \Drupal\Core\Routing\RouteBuilderInterface
+   */
+  private $routeBuilder;
+
+  /**
+   * {@inheritdoc}.
    */
   public function getFormId() {
     return 'rename_admin_paths_settings_form';
@@ -27,51 +32,73 @@ class RenameAdminPathsSettingsForm extends ConfigFormBase {
   }
 
   /**
-   * {@inheritdoc}
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
+   * @param \Drupal\Core\Routing\RouteBuilderInterface $routeBuilder
+   */
+  public function __construct(ConfigFactoryInterface $configFactory, RouteBuilderInterface $routeBuilder) {
+    parent::__construct($configFactory);
+
+    $this->routeBuilder = $routeBuilder;
+  }
+
+  /**
+   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+   *
+   * @return static
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('router.builder')
+    );
+  }
+
+  /**
+   * {@inheritdoc}.
    */
   public function buildForm(array $form, FormStateInterface $form_state) {
     $config = $this->config('rename_admin_paths.settings');
     $callbacks = new RenameAdminPathsCallbacks();
 
-    $form['admin_path'] = [
+    $form['admin_path'] = array(
       '#type' => 'fieldset',
-      '#title' => $this->t('Rename admin path'),
-    ];
+      '#title' => t('Rename admin path'),
+    );
 
-    $form['admin_path']['admin_path'] = [
+    $form['admin_path']['admin_path'] = array(
       '#type' => 'checkbox',
-      '#title' => $this->t('Rename admin path'),
+      '#title' => t('Rename admin path'),
       '#default_value' => $config->get('admin_path'),
-      '#description' => $this->t('If checked, "admin" will be replaced by the following term in admin path.')
-    ];
+      '#description' => t('If checked, "admin" will be replaced by the following term in admin path.')
+    );
 
-    $form['admin_path']['admin_path_value'] = [
+    $form['admin_path']['admin_path_value'] = array(
       '#type' => 'textfield',
-      '#title' => $this->t('Replace "admin" in admin path by'),
+      '#title' => t('Replace "admin" in admin path by'),
       '#default_value' => $config->get('admin_path_value'),
-      '#description' => $this->t('This value will replace "admin" in admin path.'),
-      '#element_validate' => [[$callbacks, 'validatePath']],
-    ];
+      '#description' => t('This value will replace "admin" in admin path.'),
+      '#element_validate' => array(array($callbacks, 'validatePath')),
+    );
 
-    $form['user_path'] = [
+    $form['user_path'] = array(
       '#type' => 'fieldset',
-      '#title' => $this->t('Rename user path'),
-    ];
+      '#title' => t('Rename user path'),
+    );
 
-    $form['user_path']['user_path'] = [
+    $form['user_path']['user_path'] = array(
       '#type' => 'checkbox',
-      '#title' => $this->t('Rename user path'),
+      '#title' => t('Rename user path'),
       '#default_value' => $config->get('user_path'),
-      '#description' => $this->t('If checked, "user" will be replaced by the following term in user path.'),
-    ];
+      '#description' => t('If checked, "user" will be replaced by the following term in user path.'),
+    );
 
-    $form['user_path']['user_path_value'] = [
+    $form['user_path']['user_path_value'] = array(
       '#type' => 'textfield',
-      '#title' => $this->t('Replace "user" in user path by'),
+      '#title' => t('Replace "user" in user path by'),
       '#default_value' => $config->get('user_path_value'),
-      '#description' => $this->t('This value will replace "user" in user path.'),
-      '#element_validate' => [[$callbacks, 'validatePath']],
-    ];
+      '#description' => t('This value will replace "user" in user path.'),
+      '#element_validate' => array(array($callbacks, 'validatePath')),
+    );
 
     return parent::buildForm($form, $form_state);
   }
@@ -80,16 +107,15 @@ class RenameAdminPathsSettingsForm extends ConfigFormBase {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    // Save configuration.
-    $config = \Drupal::service('config.factory')->getEditable('rename_admin_paths.settings');
+    // Save configuration
+    $config = $this->configFactory()->getEditable('rename_admin_paths.settings');
     $config->set('admin_path', $form_state->getValue('admin_path'));
     $config->set('admin_path_value', $form_state->getValue('admin_path_value'));
     $config->set('user_path', $form_state->getValue('user_path'));
     $config->set('user_path_value', $form_state->getValue('user_path_value'));
     $config->save();
 
-    // Rebuild routes.
-    \Drupal::service('router.builder')->rebuild();
+    // Rebuild routes
+    $this->routeBuilder->rebuild();
   }
-
 }
diff --git a/src/RenameAdminPathsProcessor.php b/src/RenameAdminPathsProcessor.php
deleted file mode 100644
index 384dbbd..0000000
--- a/src/RenameAdminPathsProcessor.php
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-
-namespace Drupal\rename_admin_paths;
-
-use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
-use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
-use Drupal\Core\Render\BubbleableMetadata;
-use Symfony\Component\HttpFoundation\Request;
-
-/**
- * Path processor for url_alter_test.
- */
-class RenameAdminPathsProcessor implements InboundPathProcessorInterface, OutboundPathProcessorInterface {
-
-  /**
-   * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound().
-   */
-  public function processInbound($path, Request $request) {
-    $config = \Drupal::config('rename_admin_paths.settings');
-
-    // Admin path.
-    if ($config->get('admin_path')) {
-      $admin_path_value = $config->get('admin_path_value');
-
-      // 404 for default admin path.
-      if (preg_match('|^/admin(?![^/])|i', $path)) {
-        $path = '/404';
-      }
-      // Get back default admin path.
-      elseif (preg_match('|^/' . urlencode($admin_path_value) . '(?![^/])(.*)|', $path, $matches)) {
-        $path = '/admin' . $matches[1];
-      }
-    }
-
-    // User path.
-    if ($config->get('user_path')) {
-      $user_path_value = $config->get('user_path_value');
-
-      // 404 for default user path.
-      if (preg_match('|^/user(?![^/])|i', $path)) {
-        $path = '/404';
-      }
-      // Get back default user path.
-      elseif (preg_match('|^/' . urlencode($user_path_value) . '(?![^/])(.*)|', $path, $matches)) {
-        $path = '/user' . $matches[1];
-      }
-    }
-
-    return $path;
-  }
-
-  /**
-   * Implements Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound().
-   */
-  public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
-    $config = \Drupal::config('rename_admin_paths.settings');
-
-    // Admin path.
-    if ($request && $config->get('admin_path')) {
-      $admin_path_value = $config->get('admin_path_value');
-
-      // Replace admin in path.
-      if (preg_match('|^/admin(?![^/])(.*)|', $path, $matches)) {
-        $path = '/' . urlencode($admin_path_value) . $matches[1];
-      }
-    }
-
-    // User path.
-    if ($config->get('user_path')) {
-      $user_path_value = $config->get('user_path_value');
-
-      // Replace user in path.
-      if (preg_match('|^/user(?![^/])(.*)|', $path, $matches)) {
-        $path = '/' . urlencode($user_path_value) . $matches[1];
-      }
-    }
-
-    return $path;
-  }
-
-}
diff --git a/tests/src/Unit/EventSubscriber/RenameAdminPathEventSubscriberTest.php b/tests/src/Unit/EventSubscriber/RenameAdminPathEventSubscriberTest.php
new file mode 100644
index 0000000..0879ab2
--- /dev/null
+++ b/tests/src/Unit/EventSubscriber/RenameAdminPathEventSubscriberTest.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace Drupal\Tests\rename_admin_paths\Unit\EventSubscriber;
+
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\rename_admin_paths\EventSubscriber\RenameAdminPathEventSubscriber;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+class RenameAdminPathEventSubscriberTest extends UnitTestCase {
+
+  public function testGetSubscribedEvents() {
+    $events = RenameAdminPathEventSubscriber::getSubscribedEvents();
+
+    $this->assertInternalType('array', $events);
+    $this->assertCount(1, $events);
+  }
+
+  public function testDoNotRenamePaths() {
+    $this->assertRoutePaths(
+      [],
+      [
+        'home' => '/',
+        'about' => '/about',
+        'admin' => '/admin',
+        'admin_slashed' => '/admin/',
+        'admin_sub' => '/admin/sub',
+        'admin_sub_sub' => '/admin/sub/sub',
+        'admin_admin' => '/admin/admin',
+        'admin_sub_admin' => '/admin/sub/admin',
+        'admins' => '/admins',
+        'admins_sub' => '/admins/sub',
+        'user' => '/user',
+        'user_slashed' => '/user/',
+        'user_sub' => '/user/sub',
+        'user_sub_sub' => '/user/sub/sub',
+        'user_admin' => '/user/user',
+        'user_sub_admin' => '/user/sub/user',
+        'users' => '/users',
+        'users_sub' => '/users/sub'
+      ]
+    );
+  }
+
+  public function testRenameAdminPath() {
+    $this->assertRoutePaths(
+      [
+        'admin_path' => TRUE,
+        'admin_path_value' => 'backend',
+      ],
+      [
+        'home' => '/',
+        'about' => '/about',
+        'admin' => '/backend',
+        'admin_slashed' => '/backend/',
+        'admin_sub' => '/backend/sub',
+        'admin_sub_sub' => '/backend/sub/sub',
+        'admin_admin' => '/backend/admin',
+        'admin_sub_admin' => '/backend/sub/admin',
+        'admins' => '/admins',
+        'admins_sub' => '/admins/sub',
+        'user' => '/user',
+        'user_slashed' => '/user/',
+        'user_sub' => '/user/sub',
+        'user_sub_sub' => '/user/sub/sub',
+        'user_admin' => '/user/user',
+        'user_sub_admin' => '/user/sub/user',
+        'users' => '/users',
+        'users_sub' => '/users/sub'
+      ]
+    );
+  }
+
+  public function testRenameUserPath() {
+    $this->assertRoutePaths(
+      [
+        'user_path' => TRUE,
+        'user_path_value' => 'member',
+      ],
+      [
+        'home' => '/',
+        'about' => '/about',
+        'admin' => '/admin',
+        'admin_slashed' => '/admin/',
+        'admin_sub' => '/admin/sub',
+        'admin_sub_sub' => '/admin/sub/sub',
+        'admin_admin' => '/admin/admin',
+        'admin_sub_admin' => '/admin/sub/admin',
+        'admins' => '/admins',
+        'admins_sub' => '/admins/sub',
+        'user' => '/member',
+        'user_slashed' => '/member/',
+        'user_sub' => '/member/sub',
+        'user_sub_sub' => '/member/sub/sub',
+        'user_admin' => '/member/user',
+        'user_sub_admin' => '/member/sub/user',
+        'users' => '/users'
+      ]
+    );
+  }
+
+  public function testRenameAdminPaths() {
+    $this->assertRoutePaths(
+      [
+        'admin_path' => TRUE,
+        'admin_path_value' => 'backend',
+        'user_path' => TRUE,
+        'user_path_value' => 'member',
+      ],
+      [
+        'home' => '/',
+        'about' => '/about',
+        'admin' => '/backend',
+        'admin_slashed' => '/backend/',
+        'admin_sub' => '/backend/sub',
+        'admin_sub_sub' => '/backend/sub/sub',
+        'admin_admin' => '/backend/admin',
+        'admin_sub_admin' => '/backend/sub/admin',
+        'admins' => '/admins',
+        'admins_sub' => '/admins/sub',
+        'user' => '/member',
+        'user_slashed' => '/member/',
+        'user_sub' => '/member/sub',
+        'user_sub_sub' => '/member/sub/sub',
+        'user_admin' => '/member/user',
+        'user_sub_admin' => '/member/sub/user',
+        'users' => '/users',
+        'users_sub' => '/users/sub'
+      ]
+    );
+  }
+
+  /**
+   * @param array $config
+   * @param array $routes
+   */
+  private function assertRoutePaths(array $config, array $routes) {
+    $route_collection = $this->getRouteCollection();
+
+    $event_subscriber = new RenameAdminPathEventSubscriber(
+      $this->getConfigFactoryStub(
+        [
+          'rename_admin_paths.settings' => $config,
+        ]
+      )
+    );
+    $event_subscriber->onRoutesAlter(new RouteBuildEvent($route_collection));
+
+    foreach ($routes as $name => $path) {
+      $this->assertEquals($path, $route_collection->get($name)->getPath());
+    }
+  }
+
+  /**
+   * @return \Symfony\Component\Routing\RouteCollection
+   */
+  private function getRouteCollection() {
+    $route_collection = new RouteCollection();
+    foreach ([
+               'home' => '/',
+               'about' => '/about',
+               'admin' => '/admin',
+               'admin_slashed' => '/admin/',
+               'admin_sub' => '/admin/sub',
+               'admin_sub_sub' => '/admin/sub/sub',
+               'admin_admin' => '/admin/admin',
+               'admin_sub_admin' => '/admin/sub/admin',
+               'admins' => '/admins',
+               'admins_sub' => '/admins/sub',
+               'user' => '/user',
+               'user_slashed' => '/user/',
+               'user_sub' => '/user/sub',
+               'user_sub_sub' => '/user/sub/sub',
+               'user_admin' => '/user/user',
+               'user_sub_admin' => '/user/sub/user',
+               'users' => '/users',
+               'users_sub' => '/users/sub',
+             ] as $name => $path) {
+      $route_collection->add($name, new Route($path));
+    }
+
+    return $route_collection;
+  }
+}
diff --git a/tests/src/Unit/Form/RenameAdminPathsCallbacksTest.php b/tests/src/Unit/Form/RenameAdminPathsCallbacksTest.php
new file mode 100644
index 0000000..4bc80a7
--- /dev/null
+++ b/tests/src/Unit/Form/RenameAdminPathsCallbacksTest.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Drupal\Tests\rename_admin_paths\Unit\Form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\rename_admin_paths\Form\RenameAdminPathsCallbacks;
+use Drupal\Tests\UnitTestCase;
+use Prophecy\Argument;
+
+class RenameAdminPathsCallbacksTest extends UnitTestCase
+{
+  public function testValidatePathWithoutValue()
+  {
+    $element = [];
+    $this->getCallbackInstance()->validatePath($element, $this->getValidFormState());
+  }
+
+  /**
+   * @dataProvider validValues
+   */
+  public function testWithValidValue($value)
+  {
+    $element = ['#value' => $value];
+    $this->getCallbackInstance()->validatePath($element, $this->getValidFormState());
+  }
+
+  /**
+   * @dataProvider invalidValues
+   */
+  public function testWithInvalidValue($value)
+  {
+    $element = ['#value' => $value];
+    $this->getCallbackInstance()->validatePath($element, $this->getInvalidFormState());
+  }
+
+  /**
+   * @return array
+   */
+  public function validValues()
+  {
+    return [
+      ['backend'],
+      ['back-end'],
+      ['Backend'],
+      ['Back-End'],
+      ['Back_End'],
+      ['Back-End_123']
+    ];
+  }
+
+  /**
+   * @return array
+   */
+  public function invalidValues()
+  {
+    return [
+      ['backend!'],
+      ['back@end'],
+      ['(Backend)'],
+      ['Back~End'],
+      ['Back=End'],
+      ['Back-End+123']
+    ];
+  }
+
+  /**
+   * @return \Drupal\rename_admin_paths\Form\RenameAdminPathsCallbacks
+   */
+  private function getCallbackInstance()
+  {
+    $translator = $this->createMock(TranslationInterface::class);
+    $translator->method('translateString')->willReturn('Error');
+
+    $callbacks = new RenameAdminPathsCallbacks();
+    $callbacks->setStringTranslation($translator);
+
+    return $callbacks;
+  }
+
+  /**
+   * @return \Drupal\Core\Form\FormStateInterface
+   */
+  private function getValidFormState()
+  {
+    $form_state = $this->prophesize(FormStateInterface::class);
+    $form_state->setError()->shouldNotBeCalled();
+
+    return $form_state->reveal();
+  }
+
+  /**
+   * @return \Drupal\Core\Form\FormStateInterface
+   */
+  private function getInvalidFormState()
+  {
+    $form_state = $this->prophesize(FormStateInterface::class);
+    $form_state->setError(Argument::any(), Argument::any())->shouldBeCalled();
+
+    return $form_state->reveal();
+  }
+}
