diff --git a/projects/storefrontapp-e2e-cypress/cypress/e2e/ssr/product-listing-page.core-e2e.cy.ts b/projects/storefrontapp-e2e-cypress/cypress/e2e/ssr/product-listing-page.core-e2e.cy.ts
new file mode 100644
index 00000000000..295e1211259
--- /dev/null
+++ b/projects/storefrontapp-e2e-cypress/cypress/e2e/ssr/product-listing-page.core-e2e.cy.ts
@@ -0,0 +1,69 @@
+/*
+ * SPDX-FileCopyrightText: 2023 SAP Spartacus team <spartacus-team@sap.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { SSR_E2E_PLP_SCENARIOS } from '../../helpers/ssr/product-listing-page';
+
+const SEARCH_REQUEST_URL = '**/products/search?**';
+const scenarios = SSR_E2E_PLP_SCENARIOS;
+
+describe('SSR - Product Listing Page', () => {
+  /**
+   * This tests all the scenarios where SSR should render the page but the search request is
+   * only made on the initial page load. The server should have rendered the page and return
+   * the cached page that does NOT need to request the search api again on reload.
+   *
+   * Note: When in development, restarting the dev ssr server (npm run dev:ssr) may be required
+   * to clear the rendering cache.
+   */
+  describe('search request should only be made once and NOT on page reload', () => {
+    for (let scenario of scenarios) {
+      // Skip is used in case of going back to a page that would be already cached
+      // since another search request would NOT be made.
+      if (!scenario.skipReloadTest) {
+        it(scenario.case, () => {
+          cy.intercept(SEARCH_REQUEST_URL).as('search-init');
+          cy.visit(scenario.url);
+          cy.wait('@search-init');
+
+          cy.intercept(SEARCH_REQUEST_URL, cy.spy().as('search-2nd'));
+          cy.reload();
+          cy.get('cx-product-list');
+          cy.get('@search-2nd').should('not.have.been.called');
+        });
+      }
+    }
+  });
+
+  /**
+   * This tests that navigation has not broken when navigating options such as paginations and sorts.
+   */
+  describe(
+    'should be able to navigate through all scenarios and trigger requests for each case',
+    { testIsolation: false },
+    () => {
+      for (let i = 0; i < scenarios.length; i++) {
+        const scenario = scenarios[i];
+        const previous = scenarios[i - 1];
+        it(scenario.case, () => {
+          // Visit whenever no next step from previous scenario to begin with new search type.
+          if (!previous?.navigateToNext) {
+            cy.visit(scenarios[i].url);
+          }
+
+          cy.get('cx-product-list');
+          cy.url().should('contain', scenario.url);
+
+          // Make sure navigation has happened successfully by checking a search request was made.
+          if (scenario.navigateToNext) {
+            cy.intercept(SEARCH_REQUEST_URL).as('search');
+            scenario.navigateToNext();
+            cy.wait('@search');
+          }
+        });
+      }
+    }
+  );
+});
diff --git a/projects/storefrontapp-e2e-cypress/cypress/helpers/ssr/product-listing-page.ts b/projects/storefrontapp-e2e-cypress/cypress/helpers/ssr/product-listing-page.ts
new file mode 100644
index 00000000000..d01a9381e42
--- /dev/null
+++ b/projects/storefrontapp-e2e-cypress/cypress/helpers/ssr/product-listing-page.ts
@@ -0,0 +1,96 @@
+/*
+ * SPDX-FileCopyrightText: 2023 SAP Spartacus team <spartacus-team@sap.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+const CASE_TITLES = {
+  ALL_BRANDS: 'All Brands',
+  QUERY_GRIP: 'Search Query "grip"',
+  DIGITAL_CAMERAS: 'Digital Cameras',
+};
+
+const CASE_URLS = {
+  ALL_BRANDS: '/Brands/all/c/brands',
+  QUERY_GRIP: '/search/grip',
+  DIGITAL_CAMERAS: '/Open-Catalogue/Cameras/Digital-Cameras/c/575',
+};
+
+const CASE_QUERY_PARTS = {
+  ALL_BRANDS: '?query=:topRated:allCategories:brands',
+  QUERY_GRIP: '?query=grip:topRated',
+  DIGITAL_CAMERAS: '?query=:topRated:allCategories:575',
+};
+
+function getStandardCases(key: string) {
+  return [
+    {
+      case: CASE_TITLES[key],
+      url: CASE_URLS[key],
+      navigateToNext: () => {
+        cy.get('cx-pagination a.page[aria-label="page 2"]').first().click();
+      },
+    },
+    {
+      case: CASE_TITLES[key] + ' (2nd page)',
+      url: CASE_URLS[key] + '?currentPage=1',
+      navigateToNext: () => {
+        cy.get('cx-pagination a.start[aria-label="first page"]')
+          .first()
+          .click();
+      },
+    },
+    {
+      case: CASE_TITLES[key] + ' (back to first page)',
+      url: CASE_URLS[key],
+      navigateToNext: () => {
+        cy.get('cx-sorting .ng-select').first().ngSelect('Top Rated');
+      },
+      skipReloadTest: true,
+    },
+    {
+      case: CASE_TITLES[key] + ' (with sort)',
+      url: CASE_URLS[key] + '?sortCode=topRated',
+      navigateToNext: () => {
+        cy.get('cx-pagination a.page[aria-label="page 2"]').first().click();
+      },
+    },
+    {
+      case: CASE_TITLES[key] + ' (2nd page with sort)',
+      url: CASE_URLS[key] + '?sortCode=topRated&currentPage=1',
+      navigateToNext: () => {
+        cy.get('cx-facet a').contains('Chiba').click();
+      },
+    },
+    {
+      case: CASE_TITLES[key] + ' (with query and sort)',
+      url: CASE_URLS[key] + CASE_QUERY_PARTS[key] + ':availableInStores:Chiba',
+      navigateToNext: () => {
+        cy.get('cx-pagination a.page[aria-label="page 2"]').first().click();
+      },
+    },
+    {
+      case: CASE_TITLES[key] + ' (2nd page with query and sort)',
+      url:
+        CASE_URLS[key] +
+        CASE_QUERY_PARTS[key] +
+        ':availableInStores:Chiba&currentPage=1',
+      navigateToNext: () => {
+        cy.get('cx-sorting .ng-select').first().ngSelect('Relevance');
+      },
+    },
+    {
+      case: CASE_TITLES[key] + ' (with query changing sort to default)',
+      url:
+        CASE_URLS[key] +
+        CASE_QUERY_PARTS[key] +
+        ':availableInStores:Chiba&currentPage=1&sortCode=relevance',
+    },
+  ];
+}
+
+export const SSR_E2E_PLP_SCENARIOS = [
+  ...getStandardCases('ALL_BRANDS'),
+  ...getStandardCases('QUERY_GRIP'),
+  ...getStandardCases('DIGITAL_CAMERAS'),
+];
diff --git a/projects/storefrontapp-e2e-cypress/package.json b/projects/storefrontapp-e2e-cypress/package.json
index d99fb6c0a3c..220930939d5 100644
--- a/projects/storefrontapp-e2e-cypress/package.json
+++ b/projects/storefrontapp-e2e-cypress/package.json
@@ -25,7 +25,7 @@
     "cy:run:ci:ccv2-b2b": "cypress run --config-file cypress.config.ci.ts --config baseUrl=$ENDPOINT_URL_PUBLIC_SPARTACUS --reporter junit --reporter-options mochaFile=results/spartacus-test-results-[hash].xml --env API_URL=$ENDPOINT_URL_PUBLIC_API,BASE_SITE=powertools-spa,OCC_PREFIX_USER_ENDPOINT=orgUsers --spec \"cypress/e2e/cx_ccv2/regression/b2b/**/*.e2e.cy.ts\"",
     "cy:run:ci:ccv2-product-configurator": "cypress run --config-file cypress.config.ci.ts --config baseUrl=$ENDPOINT_URL_PUBLIC_SPARTACUS --reporter junit --reporter-options mochaFile=results/spartacus-test-results-[hash].xml --env API_URL=$ENDPOINT_URL_PUBLIC_API,BASE_SITE=$E2E_BASE_SITE,OCC_PREFIX_USER_ENDPOINT=orgUsers --spec $E2ES_TO_RUN",
     "cy:run:ci:mcs": "cypress run --config-file cypress.config.ci.ts --config baseUrl=$ENDPOINT_URL_PUBLIC_SPARTACUS --reporter junit --reporter-options mochaFile=results/spartacus-test-results-[hash].xml --env API_URL=$ENDPOINT_URL_PUBLIC_API --spec \"cypress/e2e/cx_mcs/regression/b2c/**/*e2e.cy.ts\"",
-    "cy:run:ci:ssr": "cypress run --config-file cypress.config.ci.ts --config baseUrl=http://localhost:4000 --record --key $CYPRESS_KEY --tag \"ssr,all\" --parallel --ci-build-id $BUILD_NUMBER --group SSR --spec \"cypress/e2e/ssr/pages.core-e2e.cy.ts\" --reporter junit --reporter-options mochaFile=results/e2e-test-ssr-[hash].xml",
+    "cy:run:ci:ssr": "cypress run --config-file cypress.config.ci.ts --config baseUrl=http://localhost:4000 --record --key $CYPRESS_KEY --tag \"ssr,all\" --parallel --ci-build-id $BUILD_NUMBER --group SSR --spec \"cypress/e2e/ssr/*.core-e2e.cy.ts\" --reporter junit --reporter-options mochaFile=results/e2e-test-ssr-[hash].xml",
     "cy:run:ci:cds": "cypress run --config-file cypress.config.ci.ts --record --key $CYPRESS_KEY --tag \"2011,b2c,all-cds\" --group CDS --spec \"cypress/e2e/vendor/cds/**/*.core-e2e.cy.ts\"",
     "cy:run:ci:cdc": "cypress run --config-file cypress.config.ci.ts --env API_URL=https://api.cg79x9wuu9-eccommerc1-s1-public.model-t.myhybris.cloud/ --record --key $CYPRESS_KEY --tag \"2005,cdc\" --group CDC --spec \"cypress/e2e/vendor/cdc/b2c/*.e2e.cy.ts\"",
     "cy:run:ci:cdc-b2b": "cypress run --config-file cypress.ci.json --env BASE_SITE=powertools-spa,OCC_PREFIX_USER_ENDPOINT=orgUsers,API_URL=https://api.cg79x9wuu9-eccommerc1-s1-public.model-t.myhybris.cloud/ --record --key $CYPRESS_KEY --tag \"2211,cdc\" --group CDC --spec \"cypress/integration/vendor/cdc/b2b/*.e2e.cy.ts\"",
diff --git a/projects/storefrontlib/cms-components/product/product-list/container/product-list-component.service.spec.ts b/projects/storefrontlib/cms-components/product/product-list/container/product-list-component.service.spec.ts
index a8679cbf29d..127ac3ed0b8 100644
--- a/projects/storefrontlib/cms-components/product/product-list/container/product-list-component.service.spec.ts
+++ b/projects/storefrontlib/cms-components/product/product-list/container/product-list-component.service.spec.ts
@@ -3,6 +3,7 @@ import { ActivatedRoute, Router } from '@angular/router';
 import {
   ActivatedRouterStateSnapshot,
   CurrencyService,
+  FeatureConfigService,
   LanguageService,
   ProductSearchPage,
   ProductSearchService,
@@ -52,6 +53,12 @@ class MockLanguageService {
   }
 }
 
+class MockFeatureConfigService implements Partial<FeatureConfigService> {
+  isLevel(): boolean {
+    return true;
+  }
+}
+
 describe('ProductListComponentService', () => {
   let service: ProductListComponentService;
   let activatedRoute: ActivatedRoute;
@@ -82,6 +89,7 @@ describe('ProductListComponentService', () => {
         { provide: ProductSearchService, useClass: MockProductSearchService },
         { provide: CurrencyService, useClass: MockCurrencyService },
         { provide: LanguageService, useClass: MockLanguageService },
+        { provide: FeatureConfigService, useClass: MockFeatureConfigService },
         provideDefaultConfig(<ViewConfig>defaultViewConfig),
       ],
     });
@@ -290,5 +298,215 @@ describe('ProductListComponentService', () => {
         );
       }));
     });
+
+    describe('should perform search ONLY if product data does not already exist (state transfered by SSR)', () => {
+      it('by default', fakeAsync(() => {
+        mockRoutingState({});
+        productSearchService.getResults = () =>
+          of({
+            pagination: {
+              pageSize: 12,
+            },
+          });
+
+        const subscription: Subscription = service.model$.subscribe();
+
+        tick();
+
+        subscription.unsubscribe();
+
+        expect(productSearchService.search).not.toHaveBeenCalled();
+      }));
+
+      it('param "categoryCode"', fakeAsync(() => {
+        mockRoutingState({
+          params: { categoryCode: 'testCategory' },
+        });
+        productSearchService.getResults = () =>
+          of({
+            currentQuery: {
+              query: { value: 'relevance:allCategories:testCategory' },
+            },
+            pagination: {
+              pageSize: 12,
+            },
+          });
+
+        const subscription: Subscription = service.model$.subscribe();
+
+        tick();
+
+        subscription.unsubscribe();
+
+        expect(productSearchService.search).not.toHaveBeenCalled();
+      }));
+
+      it('param "brandCode"', fakeAsync(() => {
+        mockRoutingState({
+          params: { brandCode: 'testBrand' },
+        });
+        productSearchService.getResults = () =>
+          of({
+            currentQuery: {
+              query: { value: 'relevance:allCategories:testBrand' },
+            },
+            pagination: {
+              pageSize: 12,
+            },
+          });
+
+        const subscription: Subscription = service.model$.subscribe();
+
+        tick();
+
+        subscription.unsubscribe();
+
+        expect(productSearchService.search).not.toHaveBeenCalled();
+      }));
+
+      it('param "query"', fakeAsync(() => {
+        mockRoutingState({
+          params: { query: 'testQuery' },
+        });
+        productSearchService.getResults = () =>
+          of({
+            currentQuery: {
+              query: { value: 'testQuery' },
+            },
+            pagination: {
+              pageSize: 12,
+            },
+          });
+
+        const subscription: Subscription = service.model$.subscribe();
+
+        tick();
+
+        subscription.unsubscribe();
+
+        expect(productSearchService.search).not.toHaveBeenCalled();
+      }));
+
+      it('query param "query"', fakeAsync(() => {
+        mockRoutingState({
+          queryParams: { query: 'testQuery' },
+        });
+        productSearchService.getResults = () =>
+          of({
+            currentQuery: {
+              query: { value: 'testQuery' },
+            },
+            pagination: {
+              pageSize: 12,
+            },
+          });
+
+        const subscription: Subscription = service.model$.subscribe();
+
+        tick();
+
+        subscription.unsubscribe();
+
+        expect(productSearchService.search).not.toHaveBeenCalled();
+      }));
+
+      it('param "query" and query param "query"', fakeAsync(() => {
+        mockRoutingState({
+          params: { query: 'testQuery1' },
+          queryParams: { query: 'testQuery2' },
+        });
+        productSearchService.getResults = () =>
+          of({
+            currentQuery: {
+              query: { value: 'testQuery2' },
+            },
+            pagination: {
+              pageSize: 12,
+            },
+          });
+
+        const subscription: Subscription = service.model$.subscribe();
+
+        tick();
+
+        subscription.unsubscribe();
+
+        expect(productSearchService.search).not.toHaveBeenCalled();
+      }));
+
+      it('query param "currentPage"', fakeAsync(() => {
+        mockRoutingState({
+          params: { query: 'testQuery' },
+          queryParams: { currentPage: 123 },
+        });
+        productSearchService.getResults = () =>
+          of({
+            currentQuery: {
+              query: { value: 'testQuery' },
+            },
+            pagination: {
+              pageSize: 12,
+              currentPage: 123,
+            },
+          });
+
+        const subscription: Subscription = service.model$.subscribe();
+
+        tick();
+
+        subscription.unsubscribe();
+
+        expect(productSearchService.search).not.toHaveBeenCalled();
+      }));
+
+      it('query param "pageSize"', fakeAsync(() => {
+        mockRoutingState({
+          params: { query: 'testQuery' },
+          queryParams: { pageSize: 20 },
+        });
+        productSearchService.getResults = () =>
+          of({
+            currentQuery: {
+              query: { value: 'testQuery' },
+            },
+            pagination: {
+              pageSize: 20,
+            },
+          });
+
+        const subscription: Subscription = service.model$.subscribe();
+
+        tick();
+
+        subscription.unsubscribe();
+
+        expect(productSearchService.search).not.toHaveBeenCalled();
+      }));
+
+      it('query param "sortCode"', fakeAsync(() => {
+        mockRoutingState({
+          params: { query: 'testQuery' },
+          queryParams: { sortCode: 'name-asc' },
+        });
+        productSearchService.getResults = () =>
+          of({
+            currentQuery: {
+              query: { value: 'testQuery' },
+            },
+            pagination: {
+              pageSize: 12,
+              sort: 'name-asc',
+            },
+          });
+
+        const subscription: Subscription = service.model$.subscribe();
+
+        tick();
+
+        subscription.unsubscribe();
+
+        expect(productSearchService.search).not.toHaveBeenCalled();
+      }));
+    });
   });
 });
diff --git a/projects/storefrontlib/cms-components/product/product-list/container/product-list-component.service.ts b/projects/storefrontlib/cms-components/product/product-list/container/product-list-component.service.ts
index 8a19331d6f2..547f8db65bf 100644
--- a/projects/storefrontlib/cms-components/product/product-list/container/product-list-component.service.ts
+++ b/projects/storefrontlib/cms-components/product/product-list/container/product-list-component.service.ts
@@ -4,11 +4,12 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-import { Injectable } from '@angular/core';
+import { inject, Injectable } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
 import {
   ActivatedRouterStateSnapshot,
   CurrencyService,
+  FeatureConfigService,
   LanguageService,
   ProductSearchPage,
   ProductSearchService,
@@ -22,6 +23,7 @@ import {
   filter,
   map,
   shareReplay,
+  take,
   tap,
 } from 'rxjs/operators';
 import { ViewConfig } from '../../../../shared/config/view-config';
@@ -39,6 +41,11 @@ import { ProductListRouteParams, SearchCriteria } from './product-list.model';
 export class ProductListComponentService {
   protected readonly RELEVANCE_ALLCATEGORIES = ':relevance:allCategories:';
 
+  // TODO: Remove in 7.0
+  protected featureConfigService = inject(FeatureConfigService, {
+    optional: true,
+  });
+
   constructor(
     protected productSearchService: ProductSearchService,
     protected routing: RoutingService,
@@ -84,10 +91,95 @@ export class ProductListComponentService {
           state.params,
           state.queryParams
         );
-        this.search(criteria);
+
+        // TODO: Remove featureLevel condition in 7.0
+        if (this.featureConfigService?.isLevel('6.7')) {
+          this.searchIfCriteriaHasChanged(criteria);
+        } else {
+          this.search(criteria);
+        }
       })
     );
 
+  /**
+   * Search only if the previous search criteria does NOT match the new one.
+   * This prevents repeating product search calls for queries that already have loaded data.
+   */
+  protected searchIfCriteriaHasChanged(criteria: SearchCriteria) {
+    this.productSearchService
+      .getResults()
+      .pipe(take(1))
+      .subscribe((results) => {
+        const previous: SearchCriteria = {
+          query: results?.currentQuery?.query?.value,
+          currentPage: results?.pagination?.currentPage,
+          pageSize: results?.pagination?.pageSize,
+          sortCode: results?.pagination?.sort,
+        };
+
+        if (
+          checkQueriesDiffer() ||
+          checkCurrentPagesDiffer() ||
+          checkPageSizesDiffer() ||
+          checkSortCodesDiffer()
+        ) {
+          this.search(criteria);
+        }
+
+        function checkQueriesDiffer(): boolean {
+          const previousQuery = sanitizeQuery(
+            previous.query,
+            previous.sortCode
+          );
+          const currentQuery = sanitizeQuery(criteria.query, criteria.sortCode);
+          return previousQuery !== currentQuery;
+
+          // Remove sortCode portion from queries.
+          function sanitizeQuery(
+            query?: string,
+            sortCode?: string
+          ): string | undefined {
+            const DEFAULT_SORT_CODE = 'relevance';
+
+            query = query
+              ?.replace(':' + DEFAULT_SORT_CODE, '')
+              .replace(DEFAULT_SORT_CODE, '');
+
+            if (sortCode) {
+              query = query?.replace(':' + sortCode, '').replace(sortCode, '');
+            }
+
+            return query;
+          }
+        }
+
+        function checkCurrentPagesDiffer() {
+          // Can be stored as zero for previousCriteria but undefined as new criteria.
+          // We need to set these to the zero-values to perform the equivalency check.
+          const previousPage =
+            previous.currentPage && previous.currentPage > 0
+              ? previous.currentPage
+              : undefined;
+          return previousPage?.toString() !== criteria.currentPage?.toString();
+        }
+
+        function checkPageSizesDiffer() {
+          return (
+            previous.pageSize?.toString() !== criteria.pageSize?.toString()
+          );
+        }
+
+        function checkSortCodesDiffer() {
+          // Only check "sortCode" if it is defined in criteria as sortCode is often an undefined queryParam
+          // but it will always get defined as a string in previousCriteria if a search was made.
+          const previousCode = criteria.sortCode
+            ? previous?.sortCode
+            : undefined;
+          return previousCode?.toString() !== criteria.sortCode?.toString();
+        }
+      });
+  }
+
   /**
    * This stream is used for the Product Listing and Product Facets.
    *