diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index 68b7f445da5ad08aa8385791036600721ce2b185..59ab41d1e0114a8e37466642b096a551372f579b 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -411,6 +411,8 @@ class WebsiteSale(http.Controller):
 
         products_prices = lazy(lambda: products._get_sales_prices(pricelist))
 
+        fiscal_position_id = website._get_current_fiscal_position_id(request.env.user.partner_id)
+
         values = {
             'search': fuzzy_search_term or search,
             'original_search': fuzzy_search_term and search,
@@ -435,6 +437,7 @@ class WebsiteSale(http.Controller):
             'products_prices': products_prices,
             'get_product_prices': lambda product: lazy(lambda: products_prices[product.id]),
             'float_round': tools.float_round,
+            'fiscal_position_id': fiscal_position_id,
         }
         if filter_by_price_enabled:
             values['min_price'] = min_price or available_min_price
diff --git a/addons/website_sale/static/tests/tours/website_sale_fiscal_position_tour.js b/addons/website_sale/static/tests/tours/website_sale_fiscal_position_tour.js
new file mode 100644
index 0000000000000000000000000000000000000000..cfd31d7b72856aaa16d7630d1e798ea859ddbc9d
--- /dev/null
+++ b/addons/website_sale/static/tests/tours/website_sale_fiscal_position_tour.js
@@ -0,0 +1,37 @@
+odoo.define('website_sale_tour.website_sale_fiscal_position_tour', function (require) {
+    'use strict';
+
+    var tour = require("web_tour.tour");
+
+    tour.register('website_sale_fiscal_position_portal_tour', {
+        test: true,
+        url: '/shop?search=Super%20Product'
+    }, [
+    {
+        content: "Check price",
+        trigger: ".oe_product:contains('Super product') .product_price:contains('80.00')",
+        run: function() {} // Check
+    },
+    ]);
+
+    tour.register('website_sale_fiscal_position_public_tour', {
+        test: true,
+        url: '/shop?search=Super%20Product'
+    }, [
+    {
+        content: "Toggle Pricelist",
+        trigger: ".o_pricelist_dropdown > .dropdown-toggle",
+        run: 'click',
+    },
+    {
+        content: "Change Pricelist",
+        trigger: ".dropdown-item:contains('EUROPE EUR')",
+        run: 'click',
+    },
+    {
+        content: "Check price",
+        trigger: ".oe_product:contains('Super product') .product_price:contains('92.00')",
+        run: function() {} // Check
+    },
+    ]);
+});
diff --git a/addons/website_sale/tests/__init__.py b/addons/website_sale/tests/__init__.py
index 7294c8d003643d9ccb0f58ada49c5eac6f31f246..a14df15f8e1027a0a582a33fed86e3117fbbd130 100644
--- a/addons/website_sale/tests/__init__.py
+++ b/addons/website_sale/tests/__init__.py
@@ -22,3 +22,4 @@ from . import test_website_sale_product
 from . import test_website_editor
 from . import test_website_sale_reorder_from_portal
 from . import test_website_sale_snippets
+from . import test_website_sale_fiscal_position
diff --git a/addons/website_sale/tests/test_website_sale_fiscal_position.py b/addons/website_sale/tests/test_website_sale_fiscal_position.py
new file mode 100644
index 0000000000000000000000000000000000000000..a088910c5db995159da1230cae425c78fd3eb139
--- /dev/null
+++ b/addons/website_sale/tests/test_website_sale_fiscal_position.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from odoo.addons.base.tests.common import HttpCaseWithUserPortal
+from odoo.addons.product.tests.common import ProductCommon
+from odoo.tests import tagged
+
+from odoo import Command
+
+@tagged('post_install', '-at_install')
+class TestWebsiteSaleFiscalPosition(ProductCommon, HttpCaseWithUserPortal):
+
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        cls._use_currency('USD')
+
+    def test_shop_fiscal_position_products_template(self):
+        """
+            The `website_sale.products` template is computationally intensive
+            and therefore uses the cache.
+            The goal of this test is to check that this template
+            is up to date with the fiscal position detected.
+        """
+        website_id = self.env.ref('website.default_website').id
+        belgium_id = self.env.ref('base.be').id
+        # Set setting to display tax included on the website
+        config = self.env['res.config.settings'].create({})
+        config.show_line_subtotals_tax_selection = "tax_included"
+        config.execute()
+        # Create a fiscal position with a mapping of taxes
+        tax_15_excl = self.env['account.tax'].create({
+            'name': '15% excl',
+            'type_tax_use': 'sale',
+            'amount_type': 'percent',
+            'amount': 15,
+            'price_include': False,
+            'include_base_amount': False,
+        })
+        tax_0 = self.env['account.tax'].create({
+            'name': '0%',
+            'type_tax_use': 'sale',
+            'amount_type': 'percent',
+            'amount': 0,
+        })
+        self.env['account.fiscal.position'].create({
+            'name': 'fiscal_pos_belgium',
+            'auto_apply': True,
+            'country_id': belgium_id,
+            'tax_ids': [Command.create({
+                'tax_src_id': tax_15_excl.id,
+                'tax_dest_id': tax_0.id,
+            })]
+        })
+        # Create a pricelist which will be automatically detected
+        self.env['product.pricelist'].create({
+            'name': 'EUROPE EUR',
+            'selectable': True,
+            'website_id': website_id,
+            'country_group_ids': [Command.link(self.env.ref('base.europe').id)],
+            'sequence': 1,
+            'currency_id': self.env.ref('base.EUR').id,
+        })
+        # Create the product to be used for analysis
+        self.env["product.product"].create({
+            'name': "Super product",
+            'list_price': 40.00,
+            'taxes_id': [tax_15_excl.id],
+            'website_published': True,
+        })
+        # Create a conversion rate (1 USD <=> 2 EUR)
+        self.env['res.currency.rate'].search([]).unlink()
+        self.env['res.currency.rate'].create({
+            'company_id': self.env.company.id,
+            'currency_id': self.env.ref('base.EUR').id,
+            'company_rate': 2,
+            'name': '2023-01-01',
+        })
+
+        self.partner_portal.country_id = belgium_id
+
+        # [1]   By going to the shop page with the portal user,
+        #       a t-cache key `pricelist,products` + `fiscal_position_id` is generated
+        self.start_tour("/shop", 'website_sale_fiscal_position_portal_tour', login="portal")
+        # [2]   If we return to the page with a public user
+        #       and take the portal user's pricelist,
+        #       the prices must not be those previously calculated for the portal user.
+        #       Because the fiscal position differs from that of the public user.
+        self.start_tour("/shop", 'website_sale_fiscal_position_public_tour', login="")
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index 4cd92e2f5c5d07c48db5ebd68faa9aeb9ab6e432..93eba39de47b5e240557d8498406dc964c098a12 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -408,6 +408,13 @@
         </t>
     </template>
 
+    <!-- Add the fiscal position in the t-cache key after all overrides -->
+    <template id="products_fiscal_position" inherit_id="website_sale.products" priority="99">
+        <xpath expr="//div[starts-with(@t-cache, 'pricelist,products')]" position="attributes">
+            <attribute name="t-cache" add="fiscal_position_id" separator=","/>
+        </xpath>
+    </template>
+
     <!-- (Option) Products: Enable "Card" or "Thumbnails" designs -->
     <template id="products_design_card" name="Card Design" inherit_id="website_sale.products" active="False">
         <xpath expr="//table" position="attributes">