diff --git a/config/schema/redirect.schema.yml b/config/schema/redirect.schema.yml
index 1a521a6..5d83391 100644
--- a/config/schema/redirect.schema.yml
+++ b/config/schema/redirect.schema.yml
@@ -45,4 +45,4 @@ redirect.settings:
       label: 'Set Content Location Header'
     term_path_handler:
       type: boolean
-      label: 'Taxonomy Term Path Handler'
\ No newline at end of file
+      label: 'Taxonomy Term Path Handler'
diff --git a/modules/redirect_404/config/install/redirect_404.settings.yml b/modules/redirect_404/config/install/redirect_404.settings.yml
new file mode 100644
index 0000000..0d47916
--- /dev/null
+++ b/modules/redirect_404/config/install/redirect_404.settings.yml
@@ -0,0 +1 @@
+row_limit: 100
diff --git a/modules/redirect_404/config/schema/redirect_404.schema.yml b/modules/redirect_404/config/schema/redirect_404.schema.yml
new file mode 100644
index 0000000..02bdb9c
--- /dev/null
+++ b/modules/redirect_404/config/schema/redirect_404.schema.yml
@@ -0,0 +1,9 @@
+# Schema for the configuration files of the redirect_404 module.
+
+redirect_404.settings:
+  type: config_object
+  label: '404 error database logging settings'
+  mapping:
+    row_limit:
+      type: integer
+      label: '404 error database logs to keep'
diff --git a/modules/redirect_404/redirect_404.info.yml b/modules/redirect_404/redirect_404.info.yml
new file mode 100644
index 0000000..13b2334
--- /dev/null
+++ b/modules/redirect_404/redirect_404.info.yml
@@ -0,0 +1,7 @@
+name: 'Redirect 404'
+type: module
+description: 'Logs 404 errors and allows users to create redirects for often requested but missing pages.'
+core: 8.x
+
+dependencies:
+ - redirect
diff --git a/modules/redirect_404/redirect_404.install b/modules/redirect_404/redirect_404.install
new file mode 100644
index 0000000..e577c2c
--- /dev/null
+++ b/modules/redirect_404/redirect_404.install
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Update hooks for the redirect_404 module.
+ */
+
+use Drupal\Core\Language\LanguageInterface;
+
+/**
+ * Implements hook_schema().
+ */
+function redirect_404_schema() {
+  $schema['redirect_404'] = [
+    'description' => 'Stores 404 requests.',
+    'fields' => [
+      'path' => [
+        'type' => 'varchar',
+        'length' => 191,
+        'not null' => TRUE,
+        'description' => 'The path of the request',
+      ],
+      'langcode' => [
+        'description' => 'The language of this request',
+        'type' => 'varchar_ascii',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
+      ],
+      'count' => [
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The number of requests with that path and language',
+      ],
+      'timestamp' => [
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The timestamp of the last request with that path and language.',
+      ],
+      'resolved' => [
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Boolean indicating whether or not this path has a redirect assigned.',
+      ],
+    ],
+    'primary key' => ['path', 'langcode'],
+  ];
+  return $schema;
+}
diff --git a/modules/redirect_404/redirect_404.routing.yml b/modules/redirect_404/redirect_404.routing.yml
new file mode 100644
index 0000000..b019ae8
--- /dev/null
+++ b/modules/redirect_404/redirect_404.routing.yml
@@ -0,0 +1,7 @@
+redirect.fix_404:
+  path: '/admin/config/search/redirect/404'
+  defaults:
+    _title: 'Fix 404 pages'
+    _form: '\Drupal\redirect_404\Form\RedirectFix404Form'
+  requirements:
+    _permission: 'administer redirects'
diff --git a/modules/redirect_404/redirect_404.services.yml b/modules/redirect_404/redirect_404.services.yml
new file mode 100644
index 0000000..e7970c0
--- /dev/null
+++ b/modules/redirect_404/redirect_404.services.yml
@@ -0,0 +1,11 @@
+services:
+  redirect.404_subscriber:
+    class: Drupal\redirect_404\EventSubscriber\Redirect404Subscriber
+    arguments: ['@path.current', '@language_manager', '@redirect.not_found_storage']
+    tags:
+      - { name: event_subscriber }
+  redirect.not_found_storage:
+    class: Drupal\redirect_404\RedirectNotFoundStorage
+    arguments: ['@database']
+    tags:
+      - { name: backend_overridable }
diff --git a/modules/redirect_404/src/EventSubscriber/Redirect404Subscriber.php b/modules/redirect_404/src/EventSubscriber/Redirect404Subscriber.php
new file mode 100644
index 0000000..b081a27
--- /dev/null
+++ b/modules/redirect_404/src/EventSubscriber/Redirect404Subscriber.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Drupal\redirect_404\EventSubscriber;
+
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Path\CurrentPathStack;
+use Drupal\redirect_404\RedirectNotFoundStorageInterface;
+use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * An EventSubscriber that listens to redirect 404 errors.
+ */
+class Redirect404Subscriber implements EventSubscriberInterface {
+
+  /**
+   * The current path.
+   *
+   * @var \Drupal\Core\Path\CurrentPathStack
+   */
+  protected $currentPath;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * The redirect storage.
+   *
+   * @var \Drupal\redirect_404\RedirectNotFoundStorageInterface
+   */
+  protected $redirectStorage;
+
+  /**
+   * Constructs a new Redirect404Subscriber.
+   *
+   * @param \Drupal\Core\Path\CurrentPathStack $current_path
+   *   The current path.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
+   * @param \Drupal\redirect_404\RedirectNotFoundStorageInterface $redirect_storage
+   *   A redirect storage.
+   */
+  public function __construct(CurrentPathStack $current_path, LanguageManagerInterface $language_manager, RedirectNotFoundStorageInterface $redirect_storage) {
+    $this->currentPath = $current_path;
+    $this->languageManager = $language_manager;
+    $this->redirectStorage = $redirect_storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[KernelEvents::EXCEPTION][] = 'onKernelException';
+    return $events;
+  }
+
+  /**
+   * Logs an exception of 404 Redirect errors.
+   *
+   * @param GetResponseForExceptionEvent $event
+   *   Is given by the event dispatcher.
+   */
+  public function onKernelException(GetResponseForExceptionEvent $event) {
+    // Log 404 only exceptions.
+    if ($event->getException() instanceof NotFoundHttpException) {
+
+      // Write record.
+      $path = $this->currentPath->getPath();
+      $langcode = $this->languageManager->getCurrentLanguage()->getId();
+      $this->redirectStorage->logRequest($path, $langcode);
+    }
+  }
+
+}
diff --git a/modules/redirect_404/src/Form/RedirectFix404Form.php b/modules/redirect_404/src/Form/RedirectFix404Form.php
new file mode 100644
index 0000000..65e07b9
--- /dev/null
+++ b/modules/redirect_404/src/Form/RedirectFix404Form.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace Drupal\redirect_404\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Url;
+
+/**
+ * Provides a form that lists all 404 error paths and no redirect assigned yet.
+ */
+class RedirectFix404Form extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'redirect_fix_404_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $destination = $this->getDestinationArray();
+
+    $search = $this->getRequest()->get('search');
+    $form['#attributes'] = array('class' => array('search-form'));
+
+    $form['basic'] = array(
+      '#type' => 'fieldset',
+      '#title' => $this->t('Filter 404s'),
+      '#attributes' => array('class' => array('container-inline')),
+    );
+    $form['basic']['filter'] = array(
+      '#type' => 'textfield',
+      '#title' => '',
+      '#default_value' => $search,
+      '#maxlength' => 128,
+      '#size' => 25,
+    );
+    $form['basic']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => $this->t('Filter'),
+      '#action' => 'filter',
+    );
+    if ($search) {
+      $form['basic']['reset'] = array(
+        '#type' => 'submit',
+        '#value' => $this->t('Reset'),
+        '#action' => 'reset',
+      );
+    }
+
+    $languages = \Drupal::languageManager()->getLanguages(LanguageInterface::STATE_ALL);
+    $multilingual = \Drupal::languageManager()->isMultilingual();
+
+    $header = array(
+      array('data' => $this->t('Path'), 'field' => 'source'),
+      array('data' => $this->t('Count'), 'field' => 'count', 'sort' => 'desc'),
+      array('data' => $this->t('Last accessed'), 'field' => 'timestamp'),
+    );
+    if ($multilingual) {
+      $header[] = array('data' => $this->t('Language'), 'field' => 'language');
+    }
+    $header[] = array('data' => $this->t('Operations'));
+
+    /** @var \Drupal\redirect_404\RedirectNotFoundStorage $redirect_storage */
+    $redirect_storage = \Drupal::service('redirect.not_found_storage');
+    $results = $redirect_storage->listRequests($header, $search);
+
+    $rows = array();
+    foreach ($results as $result) {
+      $path = ltrim($result->path, '/');
+
+      $row = array();
+      $row['source'] = $path;
+      $row['count'] = $result->count;
+      $row['timestamp'] = \Drupal::service('date.formatter')->format($result->timestamp, 'short');
+      if ($multilingual) {
+        if (isset($languages[$result->langcode])) {
+          $row['language'] = $languages[$result->langcode]->getName();
+        }
+        else {
+          $row['language'] = $this->t('Undefined @langcode', array('@langcode' => $result->langcode));
+        }
+      }
+
+      $operations = array();
+      if (\Drupal::entityTypeManager()->getAccessControlHandler('redirect')->createAccess()) {
+        $operations['add'] = array(
+          'title' => $this->t('Add redirect'),
+          'url' => Url::fromRoute('redirect.add', [], ['query' => array('source' => $path, 'language' => $result->langcode) + $destination]),
+        );
+      }
+      $row['operations'] = array(
+        'data' => array(
+          '#type' => 'operations',
+          '#links' => $operations,
+        ),
+      );
+
+      $rows[] = $row;
+    }
+
+    $form['redirect_404_table']  = array(
+      '#theme' => 'table',
+      '#header' => $header,
+      '#rows' => $rows,
+      '#empty' => $this->t('No 404 pages without redirects found.'),
+    );
+    $form['redirect_404_pager'] = array('#type' => 'pager');
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+
+    if ($form_state->getTriggeringElement()['#action'] == 'filter') {
+      $form_state->setRedirect('redirect.fix_404', array(), array('query' => array('search' => trim($form_state->getValue('filter')))));
+    }
+    else {
+      $form_state->setRedirect('redirect.fix_404');
+    }
+  }
+
+}
diff --git a/modules/redirect_404/src/Plugin/views/field/AddRedirect.php b/modules/redirect_404/src/Plugin/views/field/AddRedirect.php
new file mode 100644
index 0000000..2f889cd
--- /dev/null
+++ b/modules/redirect_404/src/Plugin/views/field/AddRedirect.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Drupal\redirect_404\Plugin\views\field;
+
+use Drupal\Core\Url;
+use Drupal\views\Plugin\views\field\FieldPluginBase;
+use Drupal\views\ResultRow;
+
+/**
+ * Provides a views field for the 'Add redirect' button.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @ViewsField("add_redirect")
+ */
+class AddRedirect extends FieldPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function renderLink($data, ResultRow $values) {
+    $result = $this->getValue($values);
+    if (!empty($result['path']) && !empty($result['langcode'])) {
+      $path = $result['path'];
+      $language = $result['langcode'];
+
+      $link['add'] = [
+        'title' => $this->t('Add redirect'),
+        'url' => Url::fromRoute('redirect.list'),// [], ['query' => ['source' => $result['path'], 'language' => $result['langcode']) + $destination]],
+      ];
+      return drupal_render($link);
+    }
+    return $data;
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function render(ResultRow $values) {
+    $value = $this->getValue($values);
+    debug($value);
+    return $this->renderLink($this->sanitizeValue($value), $values);
+  }
+
+}
diff --git a/modules/redirect_404/src/Plugin/views/field/Language.php b/modules/redirect_404/src/Plugin/views/field/Language.php
new file mode 100644
index 0000000..4bee64d
--- /dev/null
+++ b/modules/redirect_404/src/Plugin/views/field/Language.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\redirect_404\Plugin\views\field;
+
+use Drupal\views\Plugin\views\field\LanguageField;
+use Drupal\views\ResultRow;
+
+/**
+ * Provides a views field for the 404 error language.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @ViewsField("redirect_404_language")
+ */
+class Language extends LanguageField {
+  /**
+   * {@inheritdoc}
+   */
+  public function render(ResultRow $values) {
+    $value = $this->getValue($values);
+    $languages = $this->options['native_language'] ? \Drupal::languageManager()->getNativeLanguages() : \Drupal::languageManager()->getLanguages();
+    return isset($languages[$value]) ? $languages[$value]->getName() : '';
+  }
+
+}
diff --git a/modules/redirect_404/src/Plugin/views/field/Path.php b/modules/redirect_404/src/Plugin/views/field/Path.php
new file mode 100644
index 0000000..d0f0aef
--- /dev/null
+++ b/modules/redirect_404/src/Plugin/views/field/Path.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\redirect_404\Plugin\views\field;
+
+use Drupal\views\Plugin\views\field\EntityLabel;
+use Drupal\views\ResultRow;
+
+/**
+ * Provides a views field for the 404 error path.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @ViewsField("redirect_404_path")
+ */
+class Path extends EntityLabel {
+
+  /**
+   * @inheritdoc
+   */
+  public function render(ResultRow $values) {
+    $value = $this->getValue($values);
+    return $this->sanitizeValue($value['path']);
+  }
+
+}
diff --git a/modules/redirect_404/src/Plugin/views/filter/Language.php b/modules/redirect_404/src/Plugin/views/filter/Language.php
new file mode 100644
index 0000000..2321f07
--- /dev/null
+++ b/modules/redirect_404/src/Plugin/views/filter/Language.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\redirect_404\Plugin\views\filter;
+
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\views\Plugin\views\filter\LanguageFilter;
+use Drupal\views\Plugin\views\PluginBase;
+
+/**
+ * Filter based on the 404 error page language.
+ *
+ * @ingroup views_filter_handlers
+ *
+ * @ViewsFilter("redirect_404_language_filter")
+ */
+class Language extends LanguageFilter {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValueOptions() {
+    if (!isset($this->valueOptions)) {
+      $this->valueTitle = $this->t('Language');
+      // Pass the current values so options that are already selected do not get
+      // lost when there are changes in the language configuration.
+      $this->valueOptions = $this->listLanguages(LanguageInterface::STATE_ALL | LanguageInterface::STATE_SITE_DEFAULT | PluginBase::INCLUDE_NEGOTIATED, array_keys($this->value));
+    }
+    return $this->valueOptions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query() {
+    // Don't filter by language in case the site is not multilingual, because
+    // there is no point in doing so.
+    if (!$this->languageManager->isMultilingual()) {
+      return;
+    }
+
+    parent::query();
+  }
+
+}
diff --git a/modules/redirect_404/src/Plugin/views/filter/Path.php b/modules/redirect_404/src/Plugin/views/filter/Path.php
new file mode 100644
index 0000000..33f50ae
--- /dev/null
+++ b/modules/redirect_404/src/Plugin/views/filter/Path.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Drupal\redirect_404\Plugin\views\filter;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Path\CurrentPathStack;
+use Drupal\redirect_404\RedirectNotFoundStorageInterface;
+use Drupal\views\Plugin\views\filter\StringFilter;
+
+/**
+ * Filter based on the 404 error path.
+ *
+ * @ingroup views_filter_handlers
+ *
+ * @ViewsFilter("redirect_404_path_filter")
+ */
+class Path extends StringFilter {
+
+  /**
+   * The current path.
+   *
+   * @var \Drupal\Core\Path\CurrentPathStack
+   */
+  protected $currentPath;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * The redirect storage.
+   *
+   * @var \Drupal\redirect_404\RedirectNotFoundStorageInterface
+   */
+  protected $redirectStorage;
+
+  /**
+   * Constructs a new Redirect404Subscriber.
+   *
+   * @param \Drupal\Core\Path\CurrentPathStack $current_path
+   *   The current path.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
+   * @param \Drupal\redirect_404\RedirectNotFoundStorageInterface $redirect_storage
+   *   A redirect storage.
+   */
+  public function __construct(CurrentPathStack $current_path, LanguageManagerInterface $language_manager, RedirectNotFoundStorageInterface $redirect_storage) {
+    $this->currentPath = $current_path;
+    $this->languageManager = $language_manager;
+    $this->redirectStorage = $redirect_storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultExposeOptions() {
+    parent::defaultExposeOptions();
+    $this->options['expose']['path_filter'] = TRUE;
+  }
+  /**
+   * {@inheritdoc}
+   */
+  protected function defineOptions() {
+    $options = parent::defineOptions();
+    $options['expose']['contains']['path_filter'] = ['default' => TRUE];
+
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
+    parent::buildOptionsForm($form, $form_state);
+    $search = $this->requestStack->getCurrentRequest()->query->get('search');
+
+    $form['expose']['path_filter'] = [
+      '#type' => 'textfield',
+      '#title' => 'Path',
+      '#default_value' => $search,
+      '#maxlength' => 128,
+      '#size' => 25,
+    ];
+  }
+
+}
diff --git a/modules/redirect_404/src/Redirect404ViewsData.php b/modules/redirect_404/src/Redirect404ViewsData.php
new file mode 100644
index 0000000..7b8fba4
--- /dev/null
+++ b/modules/redirect_404/src/Redirect404ViewsData.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Drupal\redirect_404;
+
+use Drupal\views\EntityViewsData;
+
+/**
+ * Render controller for redirect_404.
+ */
+class Redirect404ViewsData extends EntityViewsData {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getViewsData() {
+    $data = parent::getViewsData();
+
+    $data['redirect_404_field_data']['add_redirect'] = [
+      'real field' => 'add',
+      'field' => [
+        'title' => 'Add redirect',
+        'help' => 'Button that goes to the Add redirect page for this data.',
+        'id' => 'add_redirect',
+      ],
+    ];
+
+    $data['redirect_404_field_data']['path'] = [
+      'title' => 'Path',
+      'help' => 'Displays the 404 error path filter.',
+      'field' => [
+        'id' => 'redirect_404_path',
+      ],
+      'filter' => [
+        'id' => 'redirect_404_path_filter',
+      ],
+    ];
+
+    $data['redirect_404_field_data']['language'] = [
+      'title' => 'Language',
+      'help' => 'Displays the 404 error language filter.',
+      'field' => [
+        'id' => 'redirect_404_language',
+      ],
+      'filter' => [
+        'id' => 'redirect_404_language_filter',
+      ],
+    ];
+
+    return $data;
+  }
+
+}
diff --git a/modules/redirect_404/src/RedirectNotFoundStorage.php b/modules/redirect_404/src/RedirectNotFoundStorage.php
new file mode 100644
index 0000000..8d1300d
--- /dev/null
+++ b/modules/redirect_404/src/RedirectNotFoundStorage.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Drupal\redirect_404;
+
+use Drupal\Core\Database\Connection;
+
+/**
+ * Controller class for redirect not found storage.
+ */
+class RedirectNotFoundStorage implements RedirectNotFoundStorageInterface {
+
+  /**
+   * Active database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * Constructs a new RedirectNotFoundStorage.
+   *
+   * @param \Drupal\Core\Database\Connection $database
+   *   A Database connection to use for reading and writing database data.
+   */
+  public function __construct(Connection $database) {
+    $this->database = $database;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function logRequest($path, $langcode) {
+    $this->database->merge('redirect_404')
+      ->key('path', $path)
+      ->key('langcode', $langcode)
+      ->expression('count', 'count + 1')
+      ->fields([
+        'timestamp' => REQUEST_TIME,
+        'count' => 1,
+        'resolved' => 0,
+      ])
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resolveLogRequest($path, $langcode) {
+    $this->database->update('redirect_404')
+      ->fields(['resolved' => 1])
+      ->condition('path', $path)
+      ->condition('langcode', $langcode)
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function listRequests(array $header = [], $search = NULL) {
+    $query = $this->database
+      ->select('redirect_404', 'r404')
+      ->extend('Drupal\Core\Database\Query\TableSortExtender')
+      ->orderByHeader($header)
+      ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
+      ->limit(25)
+      ->fields('r404');
+
+    if ($search) {
+      // Replace wildcards with PDO wildcards.
+      $wildcard = '%' . trim(preg_replace('!\*+!', '%', $this->database->escapeLike($search)), '%') . '%';
+      $query->condition('path', $wildcard, 'LIKE');
+    }
+    $results = $query->condition('resolved', 0, '=')->execute()->fetchAll();
+
+    return $results;
+  }
+
+}
diff --git a/modules/redirect_404/src/RedirectNotFoundStorageInterface.php b/modules/redirect_404/src/RedirectNotFoundStorageInterface.php
new file mode 100644
index 0000000..5f61482
--- /dev/null
+++ b/modules/redirect_404/src/RedirectNotFoundStorageInterface.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Drupal\redirect_404;
+
+/**
+ * Defines a common interface for redirect 404 classes.
+ */
+interface RedirectNotFoundStorageInterface {
+
+  /**
+   * Merges a record to the redirect_404 table.
+   *
+   * @param string $path
+   *   The path of the current request.
+   * @param string $langcode
+   *   The ID of the language code.
+   */
+  public function logRequest($path, $langcode);
+
+  /**
+   * Marks a record as resolved in the redirect_404 table.
+   *
+   * @param string $path
+   *   The path of the current request.
+   * @param string $langcode
+   *   The ID of the language code.
+   */
+  public function resolveLogRequest($path, $langcode);
+
+  /**
+   * Returns the 404 request data.
+   *
+   * @param array $header
+   *   An array containing arrays of the redirect_404 fields data.
+   * @param string $search
+   *   The search text.
+   *
+   * @return array
+   *   A list of objects with the properties:
+   *   - path
+   *   - count
+   *   - timestamp
+   *   - langcode
+   *   - resolved
+   */
+  public function listRequests(array $header = [], $search = NULL);
+
+}
diff --git a/modules/redirect_404/src/Tests/Fix404RedirectUILanguageTest.php b/modules/redirect_404/src/Tests/Fix404RedirectUILanguageTest.php
new file mode 100644
index 0000000..1b59080
--- /dev/null
+++ b/modules/redirect_404/src/Tests/Fix404RedirectUILanguageTest.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace Drupal\redirect_404\Tests;
+
+use Drupal\Core\Url;
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\redirect\Tests\RedirectUITest;
+
+/**
+ * UI tests for redirect_404 module with language and content translation.
+ *
+ * This runs the exact same tests as Fix404RedirectUITest, but with both
+ * language and content translation modules enabled.
+ *
+ * @group redirect_404
+ */
+class Fix404RedirectUILanguageTest extends RedirectUITest {
+
+  /**
+   * A user with administrative permissions.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $adminUser;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'redirect_404',
+    'node',
+    'path',
+    'dblog',
+    'views',
+    'language',
+    'content_translation',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $language = ConfigurableLanguage::createFromLangcode('de');
+    $language->save();
+    $language = ConfigurableLanguage::createFromLangcode('es');
+    $language->save();
+
+    $this->adminUser = $this->drupalCreateUser([
+      'administer redirects',
+      'access site reports',
+      'access content',
+      'bypass node access',
+      'create url aliases',
+      'administer url aliases',
+      'administer languages',
+      'administer taxonomy',
+    ]);
+  }
+
+  /**
+   * Tests the fix 404 pages workflow with language and content translation.
+   */
+  public function testFix404RedirectList() {
+    $this->drupalLogin($this->adminUser);
+
+    // Add predefined language.
+    $this->drupalPostForm('admin/config/regional/language/add', ['predefined_langcode' => 'fr'], t('Add language'));
+    $this->assertText('French');
+
+    // Visit a non existing page to have the 404 redirect_error entry.
+    $this->drupalGet('fr/testing');
+
+    $redirect = db_select('redirect_404')->fields('redirect_404')->condition('path', '/testing')->execute()->fetchAll();
+    if (count($redirect) == 0) {
+      $this->fail('No record was added');
+    }
+
+    // Go to the "fix 404" page and check the listing.
+    $this->drupalGet('admin/config/search/redirect/404');
+    $this->assertText('testing');
+    $this->assertText('French');
+    $this->clickLink(t('Add redirect'));
+
+    // Check if we generate correct Add redirect url and if the form is
+    // pre-filled.
+    $destination = Url::fromUri('base:admin/config/search/redirect/404')->toString();
+    $edit = [
+      'query' => [
+        'source' => 'testing',
+        'language' => 'fr',
+        'destination' => $destination,
+      ]
+    ];
+    $this->assertUrl('admin/config/search/redirect/add', $edit);
+    $this->assertFieldByName('redirect_source[0][path]', 'testing');
+    $this->assertOptionSelected('edit-language-0-value', 'fr');
+
+    // Save the redirect.
+    $edit = ['redirect_redirect[0][uri]' => '/node'];
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+    $this->assertUrl('admin/config/search/redirect/404');
+    $this->assertText('No 404 pages without redirects found.');
+
+    // Check if the redirect works as expected.
+    $this->assertRedirect('fr/testing', 'fr/node', 'HTTP/1.1 301 Moved Permanently');
+
+    // Test removing a redirect assignment and visit again a non existing page.
+    $this->drupalGet('admin/config/search/redirect');
+    $this->assertText('testing');
+    $this->assertText('French');
+    $this->clickLink('Delete', 0);
+    $this->drupalPostForm(NULL, [], 'Delete');
+
+    $this->assertUrl('admin/config/search/redirect');
+    $this->assertText('There is no redirect yet.');
+
+    $this->drupalGet('admin/config/search/redirect/404');
+    $this->assertText('No 404 pages without redirects found.');
+
+    // Visit again the non existing page. Should be listed again.
+    $this->drupalGet('fr/testing');
+    $this->drupalGet('admin/config/search/redirect/404');
+    $this->assertText('testing');
+    $this->assertText('French');
+    $this->clickLink('Add redirect');
+
+    // Save the redirect with a different langcode.
+    $this->assertFieldByName('redirect_source[0][path]', 'testing');
+    $this->assertOptionSelected('edit-language-0-value', 'fr');
+    $edit['language[0][value]'] = 'de';
+    $this->drupalPostForm(NULL, $edit, 'Save');
+
+    $this->assertUrl('admin/config/search/redirect/404');
+    $this->assertText('No 404 pages without redirects found.');
+
+    $this->drupalGet('admin/config/search/redirect');
+    $this->assertText('testing');
+    $this->assertText('German');
+
+    // Check if the redirect works as expected.
+    $this->assertRedirect('de/testing', 'de/node', 'HTTP/1.1 301 Moved Permanently');
+  }
+
+}
diff --git a/modules/redirect_404/src/Tests/Fix404RedirectUITest.php b/modules/redirect_404/src/Tests/Fix404RedirectUITest.php
new file mode 100644
index 0000000..51bccdd
--- /dev/null
+++ b/modules/redirect_404/src/Tests/Fix404RedirectUITest.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace Drupal\redirect_404\Tests;
+
+use Drupal\Core\Url;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * UI tests for redirect_404 module.
+ *
+ * @group redirect_404
+ */
+class Fix404RedirectUITest extends WebTestBase {
+
+  /**
+   * A user with administrative permissions.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $adminUser;
+
+  /**
+   * The redirect entity repository.
+   *
+   * @var \Drupal\redirect\RedirectRepository
+   */
+  protected $repository;
+
+  /**
+   * The content entity database storage used in this test.
+   *
+   * @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage
+   */
+  protected $storage;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'redirect_404',
+    'node',
+    'path',
+    'dblog',
+    'views',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
+    $this->adminUser = $this->drupalCreateUser([
+      'administer redirects',
+      'access site reports',
+      'access content',
+      'bypass node access',
+      'create url aliases',
+      'administer url aliases',
+    ]);
+
+    $this->repository = \Drupal::service('redirect.repository');
+    $this->storage = $this->container->get('entity.manager')->getStorage('redirect');
+  }
+
+  /**
+   * Tests the fix 404 pages workflow.
+   */
+  public function testFix404Pages() {
+    $this->drupalLogin($this->adminUser);
+
+    // Visit a non existing page to have the 404 redirect_error entry.
+    $this->drupalGet('non-existing');
+
+    // Go to the "fix 404" page and check the listing.
+    $this->drupalGet('admin/config/search/redirect/404');
+    $this->assertText('non-existing');
+    $this->clickLink(t('Add redirect'));
+
+    // Check if we generate correct Add redirect url and if the form is
+    // pre-filled.
+    $destination = Url::fromUri('base:admin/config/search/redirect/404')->toString();
+    $edit = [
+      'query' => [
+        'source' => 'non-existing',
+        'language' => 'en',
+        'destination' => $destination,
+      ]
+    ];
+    $this->assertUrl('admin/config/search/redirect/add', $edit);
+    $this->assertFieldByName('redirect_source[0][path]', 'non-existing');
+
+    // Save the redirect.
+    $this->drupalPostForm(NULL, ['redirect_redirect[0][uri]' => '/node'], t('Save'));
+    $this->assertUrl('admin/config/search/redirect/404');
+    $this->assertText('No 404 pages without redirects found.');
+
+    // Check if the redirect works as expected.
+    $this->drupalGet('non-existing');
+    $this->assertUrl('node');
+
+    // Test removing a redirect assignment and visit again a non existing page.
+    $this->drupalGet('admin/config/search/redirect');
+    $this->assertText('non-existing');
+    $this->clickLink('Delete', 0);
+    $this->drupalPostForm(NULL, [], 'Delete');
+
+    $this->assertUrl('admin/config/search/redirect');
+    $this->assertText('There is no redirect yet.');
+
+    $this->drupalGet('admin/config/search/redirect/404');
+    $this->assertText('No 404 pages without redirects found.');
+
+    // Visit again the non existing page.
+    $this->drupalGet('non-existing');
+    $this->drupalGet('admin/config/search/redirect/404');
+    $this->assertText('non-existing');
+  }
+
+}
diff --git a/redirect.install b/redirect.install
index 4acfd13..131411a 100644
--- a/redirect.install
+++ b/redirect.install
@@ -1,6 +1,7 @@
 <?php
 
 /**
+ * @file
  * Update hooks for the Redirect module.
  */
 
diff --git a/redirect.routing.yml b/redirect.routing.yml
index 36b433d..4384701 100644
--- a/redirect.routing.yml
+++ b/redirect.routing.yml
@@ -46,14 +46,6 @@ redirect.settings:
   requirements:
     _permission: 'administer redirects'
 
-redirect.fix_404:
-  path: '/admin/config/search/redirect/404'
-  defaults:
-    _title: 'Fix 404 pages'
-    _form: '\Drupal\redirect\Form\RedirectFix404Form'
-  requirements:
-    _permission: 'administer redirects'
-
 #redirect.devel_generate:
 #  requirements:
 #    _module_dependencies: 'devel'
diff --git a/src/Form/RedirectFix404Form.php b/src/Form/RedirectFix404Form.php
deleted file mode 100644
index 3dd1dda..0000000
--- a/src/Form/RedirectFix404Form.php
+++ /dev/null
@@ -1,173 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\redirect\Form\RedirectFix404Form
- */
-
-namespace Drupal\redirect\Form;
-
-use Drupal\Component\Utility\SafeMarkup;
-use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Url;
-use Symfony\Component\HttpFoundation\Request;
-
-class RedirectFix404Form extends FormBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'redirect_fix_404_form';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, FormStateInterface $form_state) {
-    $destination = drupal_get_destination();
-
-    $search = $this->getRequest()->get('search');
-    $form['#attributes'] = array('class' => array('search-form'));
-
-    // The following requires the dblog module. If it's not available, provide a message.
-    if (! \Drupal::moduleHandler()->moduleExists('dblog')) {
-      $form['message'] = [
-        '#type' => 'markup',
-        '#markup' => t('This page requires the Database Logging (dblog) module, which is currently not installed.'),
-      ];
-      return $form;
-    }
-
-    $form['basic'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Filter 404s'),
-      '#attributes' => array('class' => array('container-inline')),
-    );
-    $form['basic']['filter'] = array(
-      '#type' => 'textfield',
-      '#title' => '',
-      '#default_value' => $search,
-      '#maxlength' => 128,
-      '#size' => 25,
-    );
-    $form['basic']['submit'] = array(
-      '#type' => 'submit',
-      '#value' => t('Filter'),
-      '#action' => 'filter',
-    );
-    if ($search) {
-      $form['basic']['reset'] = array(
-        '#type' => 'submit',
-        '#value' => t('Reset'),
-        '#action' => 'reset',
-      );
-    }
-
-    $header = array(
-      array('data' => t('Page'), 'field' => 'message'),
-      array('data' => t('Count'), 'field' => 'count', 'sort' => 'desc'),
-      array('data' => t('Last accessed'), 'field' => 'timestamp'),
-      array('data' => t('Operations')),
-    );
-
-    $count_query = db_select('watchdog', 'w');
-    $count_query->addExpression('COUNT(DISTINCT(w.message))');
-    $count_query->leftJoin('redirect', 'r', 'w.message = r.redirect_source__path');
-    $count_query->condition('w.type', 'page not found');
-    $count_query->isNull('r.rid');
-    $this->filterQuery($count_query, array('w.message'), $search);
-
-    $query = db_select('watchdog', 'w')
-      ->extend('Drupal\Core\Database\Query\TableSortExtender')->orderByHeader($header)
-      ->extend('Drupal\Core\Database\Query\PagerSelectExtender')->limit(25);
-    $query->fields('w', array('message', 'variables'));
-    $query->addExpression('COUNT(wid)', 'count');
-    $query->addExpression('MAX(timestamp)', 'timestamp');
-    $query->leftJoin('redirect', 'r', 'w.message = r.redirect_source__path');
-    $query->isNull('r.rid');
-    $query->condition('w.type', 'page not found');
-    $query->groupBy('w.message');
-    $query->groupBy('w.variables');
-    $this->filterQuery($query, array('w.message'), $search);
-    $query->setCountQuery($count_query);
-    $results = $query->execute();
-
-    $rows = array();
-    foreach ($results as $result) {
-
-      // @todo Detect the language from the url.
-      $url = SafeMarkup::format($result->message, unserialize($result->variables));
-
-      $request = Request::create($url, 'GET', [], [], [], \Drupal::request()->server->all());
-      $path = ltrim($request->getPathInfo(), '/');
-
-      $row = array();
-      $row['source'] = \Drupal::l($url, Url::fromUri('base:' . $path, array('query' => $destination)));
-      $row['count'] = $result->count;
-      $row['timestamp'] = format_date($result->timestamp, 'short');
-
-      $operations = array();
-      if (\Drupal::entityManager()->getAccessControlHandler('redirect')->createAccess()) {
-        $operations['add'] = array(
-          'title' => t('Add redirect'),
-          'url' => Url::fromRoute('redirect.add', [], ['query' => array('source' => $path) + $destination]),
-        );
-      }
-      $row['operations'] = array(
-        'data' => array(
-          '#theme' => 'links',
-          '#links' => $operations,
-          '#attributes' => array('class' => array('links', 'inline', 'nowrap')),
-        ),
-      );
-
-      $rows[] = $row;
-    }
-
-    $form['redirect_404_table']  = array(
-      '#theme' => 'table',
-      '#header' => $header,
-      '#rows' => $rows,
-      '#empty' => t('No 404 pages without redirects found.'),
-    );
-    $form['redirect_404_pager'] = array('#type' => 'pager');
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-
-    if ($form_state->getTriggeringElement()['#action'] == 'filter') {
-      $form_state->setRedirect('redirect.fix_404', array(), array('query' => array('search' => trim($form_state->getValue('filter')))));
-    }
-    else {
-      $form_state->setRedirect('redirect.fix_404');
-    }
-  }
-
-  /**
-   * Extends a query object for URL redirect filters.
-   *
-   * @param $query
-   *   Query object that should be filtered.
-   * @param $keys
-   *   The filter string to use.
-   */
-  protected function filterQuery(SelectInterface $query, array $fields, $keys = '') {
-    if ($keys && $fields) {
-      // Replace wildcards with PDO wildcards.
-      $conditions = db_or();
-      $wildcard = '%' . trim(preg_replace('!\*+!', '%', db_like($keys)), '%') . '%';
-      foreach ($fields as $field) {
-        $conditions->condition($field, $wildcard, 'LIKE');
-      }
-      $query->condition($conditions);
-    }
-  }
-
-}
diff --git a/src/Form/RedirectForm.php b/src/Form/RedirectForm.php
index 1cee07b..9dbc4c9 100644
--- a/src/Form/RedirectForm.php
+++ b/src/Form/RedirectForm.php
@@ -142,6 +142,14 @@ class RedirectForm extends ContentEntityForm {
    * {@inheritdoc}
    */
   public function save(array $form, FormStateInterface $form_state) {
+    // Update 'resolved' field in redirect_404 table, if its module is enabled.
+    $path = '/' . $this->requestStack->getCurrentRequest()->query->get('source');
+    $langcode = $this->requestStack->getCurrentRequest()->query->get('language');
+    $redirect = $form_state->getValue(['redirect_redirect', 0]);
+    if (\Drupal::moduleHandler()->moduleExists('redirect_404') && !empty($redirect['uri'])) {
+      \Drupal::service('redirect.not_found_storage')->resolveLogRequest($path, $langcode);
+    }
+
     $this->entity->save();
     drupal_set_message(t('The redirect has been saved.'));
     $form_state->setRedirect('redirect.list');
diff --git a/src/RedirectStorageSchema.php b/src/RedirectStorageSchema.php
index acdb1d9..b080c79 100644
--- a/src/RedirectStorageSchema.php
+++ b/src/RedirectStorageSchema.php
@@ -27,9 +27,8 @@ class RedirectStorageSchema extends SqlContentEntityStorageSchema {
     ];
     $schema['redirect']['indexes'] += [
       // Limit length to 191.
-      'source_language' => [['redirect_source__path', 191],'language'],
+      'source_language' => [['redirect_source__path', 191], 'language'],
     ];
-
     return $schema;
   }
 
diff --git a/src/Tests/RedirectUITest.php b/src/Tests/RedirectUITest.php
index e2ace54..bae2e0d 100644
--- a/src/Tests/RedirectUITest.php
+++ b/src/Tests/RedirectUITest.php
@@ -230,35 +230,6 @@ class RedirectUITest extends WebTestBase {
   }
 
   /**
-   * Tests the fix 404 pages workflow.
-   */
-  public function testFix404Pages() {
-    $this->drupalLogin($this->adminUser);
-
-    // Visit a non existing page to have the 404 watchdog entry.
-    $this->drupalGet('non-existing');
-
-    // Go to the "fix 404" page and check the listing.
-    $this->drupalGet('admin/config/search/redirect/404');
-    $this->assertText('non-existing');
-    $this->clickLink(t('Add redirect'));
-
-    // Check if we generate correct Add redirect url and if the form is
-    // pre-filled.
-    $destination = Url::fromUri('base:admin/config/search/redirect/404')->toString();
-    $this->assertUrl('admin/config/search/redirect/add', ['query' => ['source' => 'non-existing', 'destination' => $destination]]);
-    $this->assertFieldByName('redirect_source[0][path]', 'non-existing');
-
-    // Save the redirect.
-    $this->drupalPostForm(NULL, array('redirect_redirect[0][uri]' => '/node'), t('Save'));
-    $this->assertUrl('admin/config/search/redirect/404');
-
-    // Check if the redirect works as expected.
-    $this->drupalGet('non-existing');
-    $this->assertUrl('node');
-  }
-
-  /**
    * Tests redirects being automatically created upon path alias change.
    */
   public function testAutomaticRedirects() {
