diff --git a/addons/account/tests/test_portal_attachment.py b/addons/account/tests/test_portal_attachment.py
index 182c0d33a9b442936068ea813ff98f454efd23f2..584d8e8b2595976eba7baa9df2596636e98a2ea7 100644
--- a/addons/account/tests/test_portal_attachment.py
+++ b/addons/account/tests/test_portal_attachment.py
@@ -10,7 +10,7 @@ from odoo.tools import mute_logger
 @tests.tagged('post_install', '-at_install')
 class TestUi(tests.HttpCase):
 
-    @mute_logger('odoo.addons.website.models.ir_http', 'odoo.http')
+    @mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
     def test_01_portal_attachment(self):
         """Test the portal chatter attachment route."""
 
diff --git a/addons/http_routing/__manifest__.py b/addons/http_routing/__manifest__.py
index f4636332f45eb46c9c76315fa2e6f36d5bfc7181..f93dcdf96dc9e16283edabfb4a053500e14174ca 100644
--- a/addons/http_routing/__manifest__.py
+++ b/addons/http_routing/__manifest__.py
@@ -11,6 +11,7 @@ Proposes advanced routing options not available in web or base to keep
 base modules simple.
 """,
     'data': [
+        'views/http_routing_template.xml',
         'views/res_lang_views.xml',
     ],
     'depends': ['web'],
diff --git a/addons/http_routing/models/ir_http.py b/addons/http_routing/models/ir_http.py
index 71d62d054dbaf2788aa9be2ac6f14ba46ba3f063..06d2ee87aa848c7cc490ba2731d554c235aa2977 100644
--- a/addons/http_routing/models/ir_http.py
+++ b/addons/http_routing/models/ir_http.py
@@ -5,6 +5,7 @@ import json
 import logging
 import os
 import re
+import traceback
 import unicodedata
 import werkzeug
 
@@ -15,8 +16,9 @@ except ImportError:
     slugify_lib = None
 
 import odoo
-from odoo import api, models
+from odoo import api, models, registry, exceptions
 from odoo.addons.base.models.ir_http import RequestUID, ModelConverter
+from odoo.addons.base.models.qweb import QWebException
 from odoo.http import request
 from odoo.osv import expression
 from odoo.tools import config, ustr, pycompat
@@ -540,3 +542,87 @@ class IrHttp(models.AbstractModel):
                 if request.httprequest.query_string:
                     path += '?' + request.httprequest.query_string.decode('utf-8')
                 return werkzeug.utils.redirect(path, code=301)
+
+    @classmethod
+    def _get_exception_code_values(cls, exception):
+        """ Return a tuple with the error code following by the values matching the exception"""
+        code = 500  # default code
+        values = dict(
+            exception=exception,
+            traceback=traceback.format_exc(),
+        )
+        # only except_orm exceptions contain a message
+        if isinstance(exception, exceptions.except_orm):
+            values['error_message'] = exception.name
+            code = 400
+            if isinstance(exception, exceptions.AccessError):
+                code = 403
+
+        elif isinstance(exception, QWebException):
+            values.update(qweb_exception=exception)
+
+            if type(exception.error) == exceptions.AccessError:
+                code = 403
+
+        elif isinstance(exception, werkzeug.exceptions.HTTPException):
+            code = exception.code
+
+        values.update(
+            status_message=werkzeug.http.HTTP_STATUS_CODES[code],
+            status_code=code,
+        )
+
+        return (code, values)
+
+    @classmethod
+    def _get_values_500_error(cls, env, values, exception):
+        values['view'] = env["ir.ui.view"]
+        return values
+
+    @classmethod
+    def _get_error_html(cls, env, code, values):
+        return env['ir.ui.view'].render_template('http_routing.%s' % code, values)
+
+    @classmethod
+    def _handle_exception(cls, exception):
+        is_frontend_request = bool(getattr(request, 'is_frontend', False))
+        if not is_frontend_request:
+            # Don't touch non frontend requests exception handling
+            return super(IrHttp, cls)._handle_exception(exception)
+        try:
+            response = super(IrHttp, cls)._handle_exception(exception)
+
+            if isinstance(response, Exception):
+                exception = response
+            else:
+                # if parent excplicitely returns a plain response, then we don't touch it
+                return response
+        except Exception as e:
+            if 'werkzeug' in config['dev_mode']:
+                raise e
+            exception = e
+
+        code, values = cls._get_exception_code_values(exception)
+
+        if code is None:
+            # Hand-crafted HTTPException likely coming from abort(),
+            # usually for a redirect response -> return it directly
+            return exception
+
+        if not request.uid:
+            cls._auth_method_public()
+        with registry(request.env.cr.dbname).cursor() as cr:
+            env = api.Environment(cr, request.uid, request.env.context)
+            if code == 500:
+                _logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
+                values = cls._get_values_500_error(env, values, exception)
+            elif code == 403:
+                _logger.warn("403 Forbidden:\n\n%s", values['traceback'])
+            elif code == 400:
+                _logger.warn("400 Bad Request:\n\n%s", values['traceback'])
+            try:
+                html = cls._get_error_html(env, code, values)
+            except Exception:
+                html = env['ir.ui.view'].render_template('http_routing.http_error', values)
+
+        return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8')
diff --git a/addons/http_routing/views/http_routing_template.xml b/addons/http_routing/views/http_routing_template.xml
new file mode 100644
index 0000000000000000000000000000000000000000..648b4c3afd88607bd0dd8963c277c24764f6521f
--- /dev/null
+++ b/addons/http_routing/views/http_routing_template.xml
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+    <template id="http_error">
+        <t t-call="web.frontend_layout">
+            <div id="wrap">
+                <div class="oe_structure">
+                    <h1 class="container mt32"><t t-esc="status_code"/>: <t t-esc="status_message"/></h1>
+                </div>
+                <t t-if="editable or debug">
+                    <t t-call="http_routing.http_error_debug"/>
+                </t>
+            </div>
+        </t>
+    </template>
+
+    <template id="error_message">
+        <p t-if="error_message">
+            <strong>Error message:</strong>
+            <pre t-esc="error_message"/>
+        </p>
+    </template>
+
+    <template id="http_error_debug">
+        <div class="container accordion mb32 mt32" id="debug_infos">
+            <div class="card" t-if="error_message">
+                <h4 class="card-header">
+                    <a data-toggle="collapse" href="#error_main">Error</a>
+                </h4>
+                <div id="error_main" class="collapse show">
+                    <div class="card-body">
+                        <t t-call="http_routing.error_message"/>
+                    </div>
+                </div>
+            </div>
+            <div class="card" t-if="qweb_exception">
+                <h4 class="card-header">
+                    <a data-toggle="collapse" href="#error_qweb">QWeb</a>
+                </h4>
+                <div id="error_qweb" class="collapse show">
+                    <div class="card-body">
+                        <p t-if="exception.message">
+                            <strong>Error message:</strong>
+                            <pre t-esc="exception.message"/>
+                        </p>
+                        <p>
+                            The error occured while rendering the template <code t-esc="qweb_exception.name"/>
+                            <t t-if="qweb_exception.html">and evaluating the following expression: <code t-esc="qweb_exception.html"/></t>
+                        </p>
+                    </div>
+                </div>
+            </div>
+            <div class="card" t-if="traceback">
+                <h4 class="card-header">
+                    <a data-toggle="collapse" href="#error_traceback">Traceback</a>
+                </h4>
+                <div id="error_traceback" t-attf-class="collapse #{not error_message and not qweb_exception and 'show'}">
+                    <div class="card-body">
+                        <pre id="exception_traceback" t-esc="traceback"/>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </template>
+
+    <template id="400">
+        <t t-call="web.frontend_layout">
+            <div id="wrap">
+                <div class="container">
+                    <h1 class="mt-5">Oops! Something went wrong.</h1>
+                    <p>Take a look at the error message below.</p>
+                </div>
+                <t t-if="editable or request.session.debug">
+                    <t t-call="http_routing.http_error_debug"/>
+                </t>
+                <t t-else="">
+                    <div class="container">
+                        <t t-call="http_routing.error_message"/>
+                    </div>
+                </t>
+        </div>
+        </t>
+    </template>
+
+    <template id="403">
+        <t t-call="web.frontend_layout">
+            <div id="wrap">
+                <div class="container">
+                    <h1 class="mt-5">403: Forbidden</h1>
+                    <p>The page you were looking for could not be authorized.</p>
+                </div>
+                    <t t-if="editable or request.session.debug">
+                        <t t-call="http_routing.http_error_debug"/>
+                    </t>
+                    <t t-else="">
+                        <div class="container">
+                            <t t-call="http_routing.error_message"/>
+                        </div>
+                    </t>
+            </div>
+        </t>
+    </template>
+
+    <template id="404">
+        <t t-call="web.frontend_layout">
+            <div id="wrap">
+                <t t-raw="0"/>
+                <div class="oe_structure oe_empty">
+                    <div class="container">
+                        <h1 class="mt-5">404: Page not found!</h1>
+                        <p>
+                            The page you were looking for could not be found; it is possible you have
+                            typed the address incorrectly, but it has most probably been removed due
+                            to the recent reorganisation.
+                        </p>
+                    </div>
+                </div>
+            </div>
+        </t>
+    </template>
+
+    <template id="500">
+        <!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
+        <!-- This template should not use any variable except those provided by http_routing.ir_http._handle_exception  -->
+        <!--    no request.crsf_token, no theme style, no assets, ... cursor can be broken during rendering !      -->
+        <!--    see test_05_reset_specific_view_controller_broken_request                                          -->
+        <!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
+        <html>
+            <head>
+                <title t-esc="status_message">Internal Server Error</title>
+                <t t-set="debug" t-value="True"/>
+
+                <link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css"/>
+                <script src="/web/static/lib/jquery/jquery.js" type="text/javascript"/>
+                <script type="text/javascript" src="/web/static/lib/bootstrap/js/util.js"/>
+                <script type="text/javascript" src="/web/static/lib/bootstrap/js/collapse.js"/>
+                <style>
+                    html {
+                        font-size: 14px;
+                    }
+                </style>
+            </head>
+            <body>
+                <div id="wrapwrap">
+                    <header>
+                        <div class="navbar navbar-expand-md navbar-light bg-light">
+                            <div class="container">
+                                <div class="collapse navbar-collapse navbar-top-collapse">
+                                    <ul class="navbar-nav ml-auto" id="top_menu">
+                                        <li class="nav-item"><a href="/" class="nav-link">Home</a></li>
+                                        <li class="nav-item"><a href="javascript: window.history.back()" class="nav-link">Back</a></li>
+                                    </ul>
+                                </div>
+                            </div>
+                        </div>
+                    </header>
+                    <main>
+                        <div id="error_message" class="oe_structure">
+                            <h2 class="container mt32"><t t-esc="status_code"/>: <t t-esc="status_message"/></h2>
+                        </div>
+
+                        <t t-if="editable or debug">
+                            <t t-call="http_routing.http_error_debug"/>
+                        </t>
+                    </main>
+                </div>
+            </body>
+        </html>
+    </template>
+</odoo>
diff --git a/addons/portal/views/portal_templates.xml b/addons/portal/views/portal_templates.xml
index 40b595518ed035e74ef60c28652ea9afb74c54c2..3547c21dfe0aaa984708dd4575fa3d89e1ccf5ef 100644
--- a/addons/portal/views/portal_templates.xml
+++ b/addons/portal/views/portal_templates.xml
@@ -493,4 +493,13 @@
             </body>
         </t>
     </template>
+
+    <template id="portal_404" inherit_id="http_routing.404">
+        <xpath expr="//p" position="after">
+            <p>Maybe you were looking for one of these popular pages ?</p>
+            <ul>
+                <li><a href="/my">Documents</a></li>
+            </ul>
+        </xpath>
+    </template>
 </odoo>
diff --git a/addons/sms/views/assets.xml b/addons/sms/views/assets.xml
index ba7f5a5914f928ed16f84c6dc4eb9653b8280667..be507fa3f9290a339f631656a6b35365cac3ef63 100644
--- a/addons/sms/views/assets.xml
+++ b/addons/sms/views/assets.xml
@@ -16,7 +16,7 @@
 
         <template id="qunit_suite" name="sms_widget_tests" inherit_id="web.qunit_suite">
             <xpath expr="." position="inside">
-                <script type="text/javascript" src="/sms/static/src/tests/sms_widget_test.js"></script>
+                <script type="text/javascript" src="/sms/static/tests/sms_widget_test.js"></script>
             </xpath>
         </template>
     </data>
diff --git a/addons/test_website/tests/test_error.py b/addons/test_website/tests/test_error.py
index 63dad4133f11b450786c0351a0f750113cee57cc..f4c9334ee9745a103f0344bb89113a5bb8c94ecd 100644
--- a/addons/test_website/tests/test_error.py
+++ b/addons/test_website/tests/test_error.py
@@ -5,6 +5,6 @@ from odoo.tools import mute_logger
 @odoo.tests.common.tagged('post_install', '-at_install')
 class TestWebsiteError(odoo.tests.HttpCase):
 
-    @mute_logger('odoo.addons.website.models.ir_http', 'odoo.http')
+    @mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.http')
     def test_01_run_test(self):
         self.start_tour("/test_error_view", 'test_error_website')
diff --git a/addons/test_website/tests/test_reset_views.py b/addons/test_website/tests/test_reset_views.py
index b9bf38013632f92f6d3550595891b75d6451c849..a11e82824c354f174a02eaaf33b102263bfceb50 100644
--- a/addons/test_website/tests/test_reset_views.py
+++ b/addons/test_website/tests/test_reset_views.py
@@ -31,7 +31,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
         self.View = self.env['ir.ui.view']
         self.test_view = self.Website.viewref('test_website.test_view')
 
-    @mute_logger('odoo.addons.website.models.ir_http')
+    @mute_logger('odoo.addons.http_routing.models.ir_http')
     def test_01_reset_specific_page_view(self):
         self.test_page_view = self.Website.viewref('test_website.test_page_view')
         total_views = self.View.search_count([('type', '=', 'qweb')])
@@ -40,7 +40,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
         self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
         self.fix_it('/test_page_view')
 
-    @mute_logger('odoo.addons.website.models.ir_http')
+    @mute_logger('odoo.addons.http_routing.models.ir_http')
     def test_02_reset_specific_view_controller(self):
         total_views = self.View.search_count([('type', '=', 'qweb')])
         # Trigger COW then break the QWEB XML on it
@@ -49,7 +49,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
         self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
         self.fix_it('/test_view')
 
-    @mute_logger('odoo.addons.website.models.ir_http')
+    @mute_logger('odoo.addons.http_routing.models.ir_http')
     def test_03_reset_specific_view_controller_t_called(self):
         self.test_view_to_be_t_called = self.Website.viewref('test_website.test_view_to_be_t_called')
 
@@ -60,7 +60,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
         self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view")
         self.fix_it('/test_view')
 
-    @mute_logger('odoo.addons.website.models.ir_http')
+    @mute_logger('odoo.addons.http_routing.models.ir_http')
     def test_04_reset_specific_view_controller_inherit(self):
         self.test_view_child_broken = self.Website.viewref('test_website.test_view_child_broken')
 
@@ -71,7 +71,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
         self.fix_it('/test_view')
 
     # This test work in real life, but not in test mode since we cannot rollback savepoint.
-    # @mute_logger('odoo.addons.website.models.ir_http', 'odoo.addons.website.models.ir_ui_view')
+    # @mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.addons.website.models.ir_ui_view')
     # def test_05_reset_specific_view_controller_broken_request(self):
     #     total_views = self.View.search_count([('type', '=', 'qweb')])
     #     # Trigger COW then break the QWEB XML on it
@@ -80,7 +80,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
     #     self.fix_it('/test_view')
 
     # also mute ir.ui.view as `get_view_id()` will raise "Could not find view object with xml_id 'not.exist'""
-    @mute_logger('odoo.addons.website.models.ir_http', 'odoo.addons.website.models.ir_ui_view')
+    @mute_logger('odoo.addons.http_routing.models.ir_http', 'odoo.addons.website.models.ir_ui_view')
     def test_06_reset_specific_view_controller_inexisting_template(self):
         total_views = self.View.search_count([('type', '=', 'qweb')])
         # Trigger COW then break the QWEB XML on it
@@ -88,14 +88,14 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
         self.assertEqual(total_views + 1, self.View.search_count([('type', '=', 'qweb')]), "Missing COW view (2)")
         self.fix_it('/test_view')
 
-    @mute_logger('odoo.addons.website.models.ir_http')
+    @mute_logger('odoo.addons.http_routing.models.ir_http')
     def test_07_reset_page_view_complete_flow(self):
         self.start_tour("/", 'test_reset_page_view_complete_flow_part1', login="admin")
         self.fix_it('/test_page_view')
         self.start_tour("/", 'test_reset_page_view_complete_flow_part2', login="admin")
         self.fix_it('/test_page_view')
 
-    @mute_logger('odoo.addons.website.models.ir_http')
+    @mute_logger('odoo.addons.http_routing.models.ir_http')
     def test_08_reset_specific_page_view_hard_mode(self):
         self.test_page_view = self.Website.viewref('test_website.test_page_view')
         total_views = self.View.search_count([('type', '=', 'qweb')])
diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py
index 6d421357c90c3b3e3c8cd8219cbacb5698d49c2a..ef9a9433ce700429014fcbfa59bbdc13dbd00b97 100644
--- a/addons/website/models/ir_http.py
+++ b/addons/website/models/ir_http.py
@@ -2,7 +2,6 @@
 # Part of Odoo. See LICENSE file for full copyright and licensing details.
 import logging
 from lxml import etree
-import traceback
 import os
 import unittest
 
@@ -14,14 +13,12 @@ import werkzeug.utils
 from functools import partial
 
 import odoo
-from odoo import api, models, registry
+from odoo import api, models
 from odoo import SUPERUSER_ID
 from odoo.http import request
-from odoo.tools import config
 from odoo.tools.safe_eval import safe_eval
 from odoo.osv.expression import FALSE_DOMAIN, OR
 
-from odoo.addons.base.models.qweb import QWebException
 from odoo.addons.http_routing.models.ir_http import ModelConverter, _guess_mimetype
 from odoo.addons.portal.controllers.portal import _build_url_w_params
 
@@ -239,7 +236,6 @@ class Http(models.AbstractModel):
                 'deletable': True,
                 'main_object': mypage,
             }, mimetype=_guess_mimetype(ext))
-        return False
 
     @classmethod
     def _serve_redirect(cls):
@@ -269,109 +265,47 @@ class Http(models.AbstractModel):
         return False
 
     @classmethod
-    def _handle_exception(cls, exception):
-        code = 500  # default code
-        is_website_request = bool(getattr(request, 'is_frontend', False) and get_request_website())
-        if not is_website_request:
-            # Don't touch non website requests exception handling
-            return super(Http, cls)._handle_exception(exception)
-        else:
+    def _get_exception_code_values(cls, exception):
+        code, values = super(Http, cls)._get_exception_code_values(exception)
+        if request.website.is_publisher() and isinstance(exception, werkzeug.exceptions.NotFound):
+            code = 'page_404'
+            values['path'] = request.httprequest.path[1:]
+        return (code, values)
+
+    @classmethod
+    def _get_values_500_error(cls, env, values, exception):
+        View = env["ir.ui.view"]
+        values = super(Http, cls)._get_values_500_error(env, values, exception)
+        if 'qweb_exception' in values:
             try:
-                response = super(Http, cls)._handle_exception(exception)
-
-                if isinstance(response, Exception):
-                    exception = response
-                else:
-                    # if parent excplicitely returns a plain response, then we don't touch it
-                    return response
-            except Exception as e:
-                if 'werkzeug' in config['dev_mode']:
-                    raise e
-                exception = e
-
-            values = dict(
-                exception=exception,
-                traceback=traceback.format_exc(),
-            )
-
-            # only except_orm exceptions contain a message
-            if isinstance(exception, odoo.exceptions.except_orm):
-                values['error_message'] = exception.name
-                code = 400
-
-            if isinstance(exception, werkzeug.exceptions.HTTPException):
-                if exception.code is None:
-                    # Hand-crafted HTTPException likely coming from abort(),
-                    # usually for a redirect response -> return it directly
-                    return exception
-                else:
-                    code = exception.code
-
-            if isinstance(exception, odoo.exceptions.AccessError):
-                code = 403
-
-            if isinstance(exception, QWebException):
-                values.update(qweb_exception=exception)
-
-                # retro compatibility to remove in 12.2
-                exception.qweb = dict(message=exception.message, expression=exception.html)
-
-                if type(exception.error) == odoo.exceptions.AccessError:
-                    code = 403
-
-            values.update(
-                status_message=werkzeug.http.HTTP_STATUS_CODES[code],
-                status_code=code,
-            )
-
-            view_id = code
-            if request.website.is_publisher() and isinstance(exception, werkzeug.exceptions.NotFound):
-                view_id = 'page_404'
-                values['path'] = request.httprequest.path[1:]
-
-            if not request.uid:
-                cls._auth_method_public()
-
-            with registry(request.env.cr.dbname).cursor() as cr:
-                env = api.Environment(cr, request.uid, request.env.context)
-                if code == 500:
-                    logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
-                    View = env["ir.ui.view"]
-                    values['view'] = View
-                    if 'qweb_exception' in values:
-                        try:
-                            # exception.name might be int, string
-                            exception_template = int(exception.name)
-                        except:
-                            exception_template = exception.name
-                        view = View._view_obj(exception_template)
-                        if exception.html and exception.html in view.arch:
-                            values['view'] = view
-                        else:
-                            # There might be 2 cases where the exception code can't be found
-                            # in the view, either the error is in a child view or the code
-                            # contains branding (<div t-att-data="request.browse('ok')"/>).
-                            et = etree.fromstring(view.with_context(inherit_branding=False).read_combined(['arch'])['arch'])
-                            node = et.xpath(exception.path)
-                            line = node is not None and etree.tostring(node[0], encoding='unicode')
-                            if line:
-                                values['view'] = View._views_get(exception_template).filtered(
-                                    lambda v: line in v.arch
-                                )
-                                values['view'] = values['view'] and values['view'][0]
-
-                        # Needed to show reset template on translated pages (`_prepare_qcontext` will set it for main lang)
-                        values['editable'] = request.uid and request.website.is_publisher()
-                elif code == 403:
-                    logger.warn("403 Forbidden:\n\n%s", values['traceback'])
-                elif code == 400:
-                    logger.warn("400 Bad Request:\n\n%s", values['traceback'])
-                try:
-                    html = env['ir.ui.view'].render_template('website.%s' % view_id, values)
-                except Exception:
-                    html = env['ir.ui.view'].render_template('website.http_error', values)
-
-            return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8')
+                # exception.name might be int, string
+                exception_template = int(exception.name)
+            except:
+                exception_template = exception.name
+            view = View._view_obj(exception_template)
+            if exception.html and exception.html in view.arch:
+                values['view'] = view
+            else:
+                # There might be 2 cases where the exception code can't be found
+                # in the view, either the error is in a child view or the code
+                # contains branding (<div t-att-data="request.browse('ok')"/>).
+                et = etree.fromstring(view.with_context(inherit_branding=False).read_combined(['arch'])['arch'])
+                node = et.xpath(exception.path)
+                line = node is not None and etree.tostring(node[0], encoding='unicode')
+                if line:
+                    values['view'] = View._views_get(exception_template).filtered(
+                        lambda v: line in v.arch
+                    )
+                    values['view'] = values['view'] and values['view'][0]
+        # Needed to show reset template on translated pages (`_prepare_qcontext` will set it for main lang)
+        values['editable'] = request.uid and request.website.is_publisher()
+        return values
+
+    @classmethod
+    def _get_error_html(cls, env, code, values):
+        if code == 'page_404':
+            return env['ir.ui.view'].render_template('website.%s' % code, values)
+        return super(Http, cls)._get_error_html(env, code, values)
 
     def binary_content(self, xmlid=None, model='ir.attachment', id=None, field='datas',
                        unique=False, filename=None, filename_field='name', download=False,
diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml
index d7a02f98437488efc01dae582a8d0f5f7061350c..30f3c8206ac09733a37ed6d066ab68de96f887ab 100644
--- a/addons/website/views/website_templates.xml
+++ b/addons/website/views/website_templates.xml
@@ -788,7 +788,7 @@
 </template>
 
 <template id="page_404">
-    <t t-call="website.404">
+    <t t-call="http_routing.404">
         <div class="container">
             <div class="alert alert-info mt32">
                 <p>This page does not exist, but you can create it as you are administrator of this site.</p>
@@ -800,256 +800,104 @@
     </t>
 </template>
 
-<template id="http_error">
-    <t t-call="website.layout">
-        <div id="wrap">
-            <div class="oe_structure">
-                <h1 class="container mt32"><t t-esc="status_code"/>: <t t-esc="status_message"/></h1>
-            </div>
-            <t t-if="editable or debug">
-                <t t-call="website.http_error_debug"/>
-            </t>
-        </div>
-    </t>
-</template>
-
-<template id="error_message">
-    <p t-if="error_message">
-        <strong>Error message:</strong>
-        <pre t-esc="error_message"/>
-    </p>
-</template>
-
-<template id="http_error_debug">
-    <div class="container accordion mb32 mt32" id="debug_infos">
-        <div class="card" t-if="error_message">
-            <h4 class="card-header">
-                <a data-toggle="collapse" href="#error_main">Error</a>
-            </h4>
-            <div id="error_main" class="collapse show">
-                <div class="card-body">
-                    <t t-call="website.error_message"/>
-                </div>
-            </div>
-        </div>
-        <div class="card" t-if="qweb_exception">
-            <h4 class="card-header">
-                <a data-toggle="collapse" href="#error_qweb">QWeb</a>
-            </h4>
-            <div id="error_qweb" class="collapse show">
-                <div class="card-body">
-                    <p t-if="exception.message">
-                        <strong>Error message:</strong>
-                        <pre t-esc="exception.message"/>
-                    </p>
-                    <p>
-                        The error occured while rendering the template <code t-esc="qweb_exception.name"/>
-                        <t t-if="qweb_exception.html">and evaluating the following expression: <code t-esc="qweb_exception.html"/></t>
-                    </p>
-                </div>
-            </div>
-        </div>
-        <div class="card" t-if="traceback">
-            <h4 class="card-header">
-                <a data-toggle="collapse" href="#error_traceback">Traceback</a>
-            </h4>
-            <div id="error_traceback" t-attf-class="collapse #{not error_message and not qweb_exception and 'show'}">
-                <div class="card-body">
-                    <pre id="exception_traceback" t-esc="traceback"/>
-                </div>
-            </div>
-        </div>
-    </div>
-</template>
-
-<template id="400">
-    <t t-call="website.layout">
-        <div id="wrap">
-            <div class="container">
-                <h1 class="mt-5">Something went wrong.</h1>
-                <p>Take a look at the error message below.</p>
-            </div>
-            <t t-if="editable or request.session.debug">
-                <t t-call="website.http_error_debug"/>
-            </t>
-            <t t-else="">
-                <div class="container">
-                    <t t-call="website.error_message"/>
-                </div>
-            </t>
-    </div>
-    </t>
-</template>
-
-<template id="403">
-    <t t-call="website.layout">
-        <div id="wrap">
-            <div class="container">
-                <h1 class="mt-5">403: Forbidden</h1>
-                <p>The page you were looking for could not be authorized.</p>
-            </div>
-                <t t-if="editable or request.session.debug">
-                    <t t-call="website.http_error_debug"/>
-                </t>
-                <t t-else="">
-                    <div class="container">
-                        <t t-call="website.error_message"/>
-                    </div>
-                </t>
-        </div>
-    </t>
-</template>
-
-<template id="404">
-    <t t-call="website.layout">
-        <div id="wrap">
-            <t t-raw="0"/>
-            <div class="oe_structure oe_empty">
-                <div class="container">
-                    <h1 class="mt-5">404: Page not found!</h1>
-                    <p>
-                        The page you were looking for could not be found; it is possible you have
-                        typed the address incorrectly, but it has most probably been removed due
-                        to the recent website reorganisation.
-                    </p>
-                    <p>Maybe you were looking for one of these popular pages ?</p>
-                    <ul>
-                        <li><a href="/">Homepage</a></li>
-                        <li><a href="/contactus">Contact Us</a></li>
-                    </ul>
-                </div>
-            </div>
-        </div>
-    </t>
-</template>
-
-<template id="500">
+<template id="qweb_500" inherit_id="http_routing.500">
     <!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
     <!-- This template should not use any variable except those provided by website.ir_http._handle_exception  -->
     <!--    no request.crsf_token, no theme style, no assets, ... cursor can be broken during rendering !      -->
     <!--    see test_05_reset_specific_view_controller_broken_request                                          -->
     <!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-    <html>
-        <head>
-            <title t-esc="status_message">Internal Server Error</title>
-            <t t-set="debug" t-value="True"/>
-
-            <link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css"/>
-            <script src="/web/static/lib/jquery/jquery.js" type="text/javascript"/>
-            <script type="text/javascript" src="/web/static/lib/bootstrap/js/util.js"/>
-            <script type="text/javascript" src="/web/static/lib/bootstrap/js/collapse.js"/>
-            <script type="text/javascript" src="/web/static/lib/bootstrap/js/modal.js"/>
-            <style>
-                html {
-                    font-size: 14px;
-                }
-            </style>
-
-            <t t-if='view'>
-                <script>
-                    $(document).ready(function() {
-                        var button = $('.reset_templates_button');
-                        button.click(function() {
-                            $('#reset_templates_mode').val($(this).data('mode'));
-                            var dialog = $('#reset_template_confirmation').modal('show');
-                            var input = dialog.find('input[type="text"]').val('').focus();
-                            var dialog_form = dialog.find('form');
-                            dialog_form.submit(function() {
-                                if (input.val() == dialog.find('.confirm_word').text()) {
-                                    dialog.modal('hide');
-                                    button.prop('disabled', true).text('Working...');
-                                    $('#reset_templates_form').attr('action', '/website/reset_template');
-                                    $('#reset_templates_form').trigger('submit');
-                                } else {
-                                    input.val('').focus();
-                                }
-                                return false;
-                            });
+    <xpath expr="//script[last()]" position="before">
+        <script type="text/javascript" src="/web/static/lib/bootstrap/js/modal.js"/>
+    </xpath>
+    <xpath expr="//style" position="after">
+        <t t-if='view'>
+            <script>
+                $(document).ready(function() {
+                    var button = $('.reset_templates_button');
+                    button.click(function() {
+                        $('#reset_templates_mode').val($(this).data('mode'));
+                        var dialog = $('#reset_template_confirmation').modal('show');
+                        var input = dialog.find('input[type="text"]').val('').focus();
+                        var dialog_form = dialog.find('form');
+                        dialog_form.submit(function() {
+                            if (input.val() == dialog.find('.confirm_word').text()) {
+                                dialog.modal('hide');
+                                button.prop('disabled', true).text('Working...');
+                                $('#reset_templates_form').attr('action', '/website/reset_template');
+                                $('#reset_templates_form').trigger('submit');
+                            } else {
+                                input.val('').focus();
+                            }
                             return false;
                         });
+                        return false;
                     });
-                </script>
-            </t>
-        </head>
-        <body>
-            <div t-if="view" role="dialog" id="reset_template_confirmation" class="modal" tabindex="-1" t-ignore="true">
-                <div class="modal-dialog">
-                    <form role="form">
-                        <div class="modal-content">
-                            <header class="modal-header">
-                                <h4 class="modal-title">Reset templates</h4>
-                                <button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>
-                            </header>
-                            <main class="modal-body">
-                                <div class="form-group row mb0">
-                                    <label for="page-name" class="col-md-12 col-form-label">
-                                        <p>The selected templates will be reset to their factory settings.</p>
-                                    </label>
-                                </div>
-                                <div class="form-group row mb0">
-                                    <label for="page-name" class="col-md-9 col-form-label">
-                                        <p>Type '<i class="confirm_word">yes</i>' in the box below if you want to confirm.</p>
-                                    </label>
-                                    <div class="col-md-3 mt16">
-                                        <input type="text" id="page-name" class="form-control" required="required" placeholder="yes"/>
-                                    </div>
+                });
+            </script>
+        </t>
+    </xpath>
+    <xpath expr="//div[@id='wrapwrap']" position="before">
+        <div t-if="view" role="dialog" id="reset_template_confirmation" class="modal" tabindex="-1" t-ignore="true">
+            <div class="modal-dialog">
+                <form role="form">
+                    <div class="modal-content">
+                        <header class="modal-header">
+                            <h4 class="modal-title">Reset templates</h4>
+                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>
+                        </header>
+                        <main class="modal-body">
+                            <div class="form-group row mb0">
+                                <label for="page-name" class="col-md-12 col-form-label">
+                                    <p>The selected templates will be reset to their factory settings.</p>
+                                </label>
+                            </div>
+                            <div class="form-group row mb0">
+                                <label for="page-name" class="col-md-9 col-form-label">
+                                    <p>Type '<i class="confirm_word">yes</i>' in the box below if you want to confirm.</p>
+                                </label>
+                                <div class="col-md-3 mt16">
+                                    <input type="text" id="page-name" class="form-control" required="required" placeholder="yes"/>
                                 </div>
-                            </main>
-                            <footer class="modal-footer">
-                                <button type="button" class="btn" data-dismiss="modal" aria-label="Cancel">Cancel</button>
-                                <input type="submit" value="Confirm" class="btn btn-primary"/>
-                            </footer>
-                        </div>
-                    </form>
-                </div>
-            </div>
-
-            <div id="wrapwrap">
-                <header>
-                    <div class="navbar navbar-expand-md navbar-light bg-light">
-                        <div class="container">
-                            <div class="collapse navbar-collapse navbar-top-collapse">
-                                <ul class="navbar-nav ml-auto" id="top_menu">
-                                    <li class="nav-item"><a href="/" class="nav-link">Home</a></li>
-                                    <li class="nav-item"><a href="javascript: window.history.back()" class="nav-link">Back</a></li>
-                                </ul>
                             </div>
-                        </div>
-                    </div>
-                </header>
-                <main>
-                    <div class="oe_structure">
-                        <h2 class="container mt32"><t t-esc="status_code"/>: <t t-esc="status_message"/></h2>
+                        </main>
+                        <footer class="modal-footer">
+                            <button type="button" class="btn" data-dismiss="modal" aria-label="Cancel">Cancel</button>
+                            <input type="submit" value="Confirm" class="btn btn-primary"/>
+                        </footer>
                     </div>
-                    <div class="container" t-if="view and editable">
-                        <div class="alert alert-danger" t-if="debug" role="alert">
-                            <h4>Template fallback</h4>
-                            <p>An error occured while rendering the template <code t-esc="qweb_exception.name"/>.</p>
-                            <p>If this error is caused by a change of yours in the templates, you have the possibility to reset the template to its <strong>factory settings</strong>.</p>
-                            <form action="#" method="post" id="reset_templates_form">
-                                <ul>
-                                    <li>
-                                        <label>
-                                            <t t-esc="view.name"/>
-                                        </label>
-                                    </li>
-                                </ul>
-                                <input type="hidden" name="redirect" t-att-value="request.httprequest.path"/>
-                                <input type="hidden" id="reset_templates_view_id" name="view_id" t-att-value="view.id"/>
-                                <input type="hidden" id="reset_templates_mode" name="mode"/>
-                                <button data-mode="soft" class="reset_templates_button btn btn-info">Restore previous version (soft reset).</button>
-                                <button t-if="view.arch_fs" data-mode="hard" class="reset_templates_button btn btn-outline-danger">Reset to initial version (hard reset).</button>
-                            </form>
-                        </div>
-                    </div>
-
-                    <t t-if="editable or debug">
-                        <t t-call="website.http_error_debug"/>
-                    </t>
-                </main>
+                </form>
             </div>
-        </body>
-    </html>
+        </div>
+    </xpath>
+    <xpath expr="//div[@id='error_message']" position="after">
+        <div class="container" t-if="view and editable">
+            <div class="alert alert-danger" t-if="debug" role="alert">
+                <h4>Template fallback</h4>
+                <p>An error occured while rendering the template <code t-esc="qweb_exception.name"/>.</p>
+                <p>If this error is caused by a change of yours in the templates, you have the possibility to reset the template to its <strong>factory settings</strong>.</p>
+                <form action="#" method="post" id="reset_templates_form">
+                    <ul>
+                        <li>
+                            <label>
+                                <t t-esc="view.name"/>
+                            </label>
+                        </li>
+                    </ul>
+                    <input type="hidden" name="redirect" t-att-value="request.httprequest.path"/>
+                    <input type="hidden" id="reset_templates_view_id" name="view_id" t-att-value="view.id"/>
+                    <input type="hidden" id="reset_templates_mode" name="mode"/>
+                    <button data-mode="soft" class="reset_templates_button btn btn-info">Restore previous version (soft reset).</button>
+                    <button t-if="view.arch_fs" data-mode="hard" class="reset_templates_button btn btn-outline-danger">Reset to initial version (hard reset).</button>
+                </form>
+            </div>
+        </div>
+    </xpath>
+</template>
+
+<template id="portal_404" inherit_id="portal.portal_404">
+    <xpath expr="//li" position="before">
+        <li><a href="/">Homepage</a></li>
+    </xpath>
 </template>
 
 <template id="robots">