# DF Faceted SEO — Faceted Search & SEO for PrestaShop

> DF Faceted SEO turns PrestaShop faceted search into a lasting SEO lever and accelerates your category pages 10 to 30 times thanks to a denormalised index. This documentation covers installation,…

- Page: <https://www.datafirefly.com/en/documentation/dffacetedseo/>
- Language: en
- Last updated: 2026-06-29
- Other languages: [fr](https://www.datafirefly.com/documentation/dffacetedseo/index.md), [es](https://www.datafirefly.com/es/documentation/dffacetedseo/index.md), [de](https://www.datafirefly.com/de/documentation/dffacetedseo/index.md), [it](https://www.datafirefly.com/it/documentation/dffacetedseo/index.md), [pl](https://www.datafirefly.com/pl/documentation/dffacetedseo/index.md), [pt](https://www.datafirefly.com/pt/documentation/dffacetedseo/index.md), [nl](https://www.datafirefly.com/nl/documentation/dffacetedseo/index.md)
- Index: <https://www.datafirefly.com/en/documentation/llms.txt>

DF Faceted SEO turns PrestaShop faceted search into a lasting SEO lever and accelerates your category pages 10 to 30 times thanks to a denormalised index. This documentation covers installation, configuration of indexation rules, creation of SEO landing pages and exploitation of popular combination statistics.

## Overview

The native `ps_facetedsearch` module mechanically generates a URL for every possible filter combination. On a catalogue of a few thousand products with five facets, that means millions of URLs that Google crawls, diluting your crawl budget and PageRank between near-duplicate pages.

DF Faceted SEO solves the problem in two parallel ways:

- **Smart indexation**: business rules decide which combinations are indexable, which receive `noindex follow`, and where the canonical points.
- **Dedicated SEO landing pages**: for combinations with strong commercial potential (e.g. "red shoes size 42"), create landing pages with a clean URL, custom H1, custom meta and editorial content per language.

As a bonus, the search engine replaces the native engine with a system that is much faster thanks to a denormalised index and a two-layer cache.

## Requirements

- PrestaShop 8.0 to 9.x
- PHP 8.1 or higher
- MySQL 5.7 or MariaDB 10.3 minimum
- Friendly URLs enabled (_Preferences → SEO and URLs_)
- Standard MySQL permissions to create tables

## Installation

1. Upload the ZIP archive via _Modules → Module Manager → Upload a module_.
2. Click **Install**. The module automatically creates 10 `ps_dffacetedseo_*` tables and 5 tabs under _Improve_.
3. Go to _Improve → DF Faceted SEO → Dashboard_.
4. Click **Rebuild index**. _This step is mandatory the first time_: without an index, the search engine has no data to work with.
5. Clear the PrestaShop cache: _Advanced parameters → Performance → Clear cache_.

On a 50,000-product catalogue, the index rebuild typically takes 2 to 5 minutes. The module inserts rows in batches of 500.

## Getting started — Dashboard

The dashboard (_Improve → DF Faceted SEO_) displays six statistic tiles:

- **Active rules**: number of enabled indexation rules
- **Landing pages**: number of SEO landing pages created
- **Products in index**: number of indexed products (should be close to your catalogue total)
- **Rows in index**: number of (product, filter value) pairs — the denormalised table
- **Cache entries**: number of cached queries
- **Popular combinations**: number of observed filter combinations

Under the tiles, two operation blocks:

- **Rebuild index**: useful after a major catalogue change (mass import, attribute structure change)
- **Clear cache**: useful after a rule or setting change

## Indexation rules

Rules decide, for every visited filter combination, which `robots` tag and which `canonical` are sent to the browser and to Google. A rule has a **scope** (where it applies) and a **policy** (the decision).

### Available scopes

- **Global**: applies to all faceted pages of the catalogue
- **Specific category**: applies only to pages of a given category
- **Attribute group**: applies to combinations containing a specific group (e.g. all pages where Colour is filtered)
- **Feature**: same, for a product feature
- **Manufacturer**: same, for a brand
- **Price range**: applies to pages filtered on a given price range

### Available policies

- **Index, follow**: indexable page, canonical pointing to itself normalised
- **Noindex, follow**: page not indexed but links are followed (recommended for rare combinations)
- **Canonical parent**: page accessible but canonical points to the parent category (SEO value is transferred)
- **Canonical self normalised**: canonical on the page itself with filters sorted and grouped (eliminates URL duplicates for the same combination)

### Priority chain

The module evaluates rules from **most specific to most generic**. Concretely, for a Shoes category page filtered by colour:

1. The module first looks for a `category`-scoped rule on Shoes
2. If it exists and matches, it is applied
3. Otherwise it looks for an `attribute_group`-scoped rule on Colour
4. Otherwise it falls back to the global rule
5. If no rule matches, the dashboard default policy is applied

### Advanced rule fields

- **Maximum indexable filters**: if the combination contains more filters than this limit, the policy automatically downgrades to `noindex follow`. Example: 2 filters maximum on shoes (colour + size indexed, but not colour + size + material + season).
- **Value whitelist**: allowed value IDs (comma-separated). A combination containing only whitelisted values is indexable. Example: whitelist colours red, black, white, blue — rare colours like "apple green" will not be indexed.
- **Value blacklist**: the opposite. Any combination containing a blacklisted value automatically falls to `noindex`.
- **Minimum products**: if the combination does not return at least this number of products, it goes to `noindex`. Avoids indexing nearly empty pages.
- **Priority**: integer. In case of conflict between two rules of the same scope, the highest priority wins.

Recommended starting strategy: create a _global_ rule with `canonical parent` policy and maximum 1 indexable filter, then progressively add more specific rules to open indexation for high-potential combinations.

## SEO landing pages

Landing pages are dedicated landing pages for strategic filter combinations. They have their own clean URL, their own H1, their own meta and editorial content per language.

### Create a landing page

1. Go to _Improve → DF Faceted SEO → Landing pages_
2. Click **Add**
3. **Targeting** section: pick the category and optionally the brand involved
4. **Display** section: default sort, direction, active, indexable, priority
5. **Filter combination** section: add the filters that compose the combination (attribute group and values, feature or brand). Example: Colour = Red AND Size = 42.
6. Multilingual tabs: for each language fill in the internal name, URL slug, H1, meta title, meta description, intro content and outro content.
7. Save

### URL structure

A landing page URL follows the pattern:

```
https://your-domain.com/landing/your-slug
https://your-domain.com/en/landing/your-slug
https://your-domain.com/fr/landing/your-slug
```

The language prefix is automatically handled by the PrestaShop router as soon as friendly URLs are enabled and several languages are active on the shop.

If you get a 404 on a landing URL, check in this order: friendly URLs enabled, `.htaccess` regenerated (Save button at the bottom of SEO and URLs), cache cleared, landing active in the form, `moduleRoutes` hook properly attached to the module in _Modules → Positions_.

### Editorial content

For each language you have two rich HTML blocks:

- **Intro**: displayed above the product grid. Ideal for a 150- to 300-word SEO introduction paragraph with your target keywords.
- **Outro**: displayed below the grid. Ideal for reassurance elements, buying guides, FAQs.

### Automatic JSON-LD

Every landing page automatically injects two JSON-LD blocks into the page:

- **BreadcrumbList**: Home → Category → Landing
- **Product**: one Product block per displayed product (name, price, image, URL, availability)

You get Google rich snippets automatically without any additional configuration.

## Popular combination statistics

The module automatically logs every filter combination actually visited by your customers in the `ps_dffacetedseo_combination_log` table: signature, hit count, last seen.

### Exploit the statistics

1. Go to _Improve → DF Faceted SEO → Statistics_
2. Filter by category, minimum hits, display limit
3. Sort by descending hits
4. Identify combinations with more than 50 hits per week
5. Click **Promote** in the action column
6. The landing page form opens **pre-filled** with the filter combination
7. You only need to fill in the editorial content and save

### Maintenance

At the bottom of the Statistics page, a _Purge old data_ button with a "days to keep" field. Recommended: purge every 90 days to avoid unlimited table growth.

## Global settings

The dashboard offers 11 global settings grouped into 3 sections.

### Indexation

- **Default policy**: policy applied when no rule matches
- **Maximum indexable filters**: global value (can be overridden per rule)
- **Minimum products per combination**: global threshold to consider a page indexable

### Performance

- **Cache TTL (seconds)**: lifetime of a cache entry. Default: 3600 (1 hour). Increase to 86400 (24 h) in stable production.
- **Hide empty filters**: if enabled, values with 0 results are hidden in the filter panel
- **Results limit per page**: product pagination

### URLs

- **Landings URL prefix**: default `landing`. The route pattern is defined hardcoded in the `moduleRoutes` hook and uses this prefix.

## Smart canonical

On every faceted page, the module computes the optimal canonical:

- Combination matching an existing landing: canonical = landing pretty URL
- Combination indexable according to rules: canonical = normalised self (filters sorted and grouped)
- Combination not indexable according to rules: canonical = parent category
- Category page without filter: canonical = standard self

Normalisation groups values by group and sorts groups by ID. Two visually different but semantically identical URLs (e.g. different GET parameter order) receive the same canonical.

## High-performance filter engine

The engine relies on the `ps_dffacetedseo_index` table: one row per (product, filter type, value) tuple. The query becomes a simple set intersection of product IDs.

### Two-layer cache

- **Level 1: memory (in-process)**. Avoids repeating the same query twice within the same page.
- **Level 2: database (ps_dffacetedseo_cache table)**. Persists results between requests with TTL.

### Automatic invalidation

The module listens to the `actionProductSave`, `actionProductDelete` and `actionCategoryUpdate` hooks. Every time a product is saved, its index entry is regenerated and all cache entries linked to that product are invalidated.

### AJAX and UX

The front filter panel is fully AJAX-driven:

- 250 ms debounce on filter changes
- `pushState` to update the URL without reloading the page
- Real-time update of the `canonical` and `robots` tags in the document head
- Smooth scroll to the product grid after update
- Active filter chips clickable to remove a single filter
- Price slider with numeric fields and Apply button
- Colour swatches visible on Colour-type attributes

## Multistore and multilingual

The module is natively multistore and multilingual:

- Every landing page has translations per language (`ps_dffacetedseo_landing_page_lang` table)
- Every landing can be restricted to one or more shops (`ps_dffacetedseo_landing_page_shop` table)
- Indexation rules apply to all shops by default
- The index is partitioned by shop
- Interface translations are provided in French and English (XLIFF in `translations/`)

## Technical architecture

### Hooks used

- `displayHeader`: dynamic canonical and meta robots
- `actionFrontControllerSetMedia`: frontend CSS and JS
- `moduleRoutes`: clean route for landing pages
- `displayLeftColumn`: filter panel injection
- `displayBeforeBodyClosingTag`: JSON-LD on landings
- `actionProductSearchProviderRunQuery`: integration with the PS engine
- `actionProductSave` and `actionProductDelete`: incremental reindexation
- `actionCategoryUpdate`: category cache invalidation
- `displayBackOfficeHeader`: admin CSS and JS

### Created tables

| Table | Role |
| --- | --- |
| `dffacetedseo_rule` | Indexation rules by scope |
| `dffacetedseo_landing_page` | Landings (business data) |
| `dffacetedseo_landing_page_lang` | Landing translations |
| `dffacetedseo_landing_page_shop` | Multistore links |
| `dffacetedseo_landing_filter` | Filters attached to landings |
| `dffacetedseo_combination_log` | Visit statistics |
| `dffacetedseo_cache` | Query result cache |
| `dffacetedseo_filter_template` | Reusable filter templates |
| `dffacetedseo_index` | Denormalised index (performance key) |
| `dffacetedseo_setting` | Internal settings |

### Filter URL convention

```
?attribute_group__{id_group}=v1,v2&feature__{id_feature}=v3&manufacturer__0=5&price=10-50&s=needle
```

A stable SHA-1 signature is computed to compare combinations (landing matching, cache key, log).

## Expected performance

Measurements on a reference catalogue (50,000 products, 8 active facets):

| Engine | Average time |
| --- | --- |
| `ps_facetedsearch` native | 800 ms to 1.4 s per query |
| DF Faceted SEO without cache | 50 to 120 ms |
| DF Faceted SEO with cache | less than 10 ms |

Typical gain: **×10 to ×30**

## Technical FAQ

### Do I need to uninstall ps_facetedsearch

No, the module integrates via the official hooks. You can test it in parallel. Once validated, uninstall `ps_facetedsearch` to avoid filter panel duplication.

### Is the module HTTPS-compatible and does it use absolute canonical URLs

Yes. The module uses `Context::getContext()->link->getPageLink()` and `getModuleLink()` which respect the PrestaShop HTTPS configuration.

### How do I add a new language on the module side

Copy the `translations/fr-FR.xlf` file to `translations/xx-XX.xlf` and translate the `trans-unit` entries. No other intervention required.

### Does the module support combination attributes

Yes. The denormalised index indexes combinations via their attributes. The search engine filters on products for which at least one combination matches all active filters.

### How do I debug a rule that does not apply

Enable PrestaShop logs (_Advanced parameters → Logs_) and check the `dffacetedseo` section. Each directive resolution logs the matched rule and the reason.

## Troubleshooting

### Fatal error on a `getBySlug` call

Symptom: `Call to a member function getBySlug() on false`. Cause: old `get()` helper returning `false` instead of the service object. Fixed in v1.0.1. Update the module.

### 404 on a landing page URL

Symptom: `https://your-site/en/landing/your-slug` returns 404. Typical cause: the `moduleRoutes` hook is not registered or the PrestaShop route cache is stale.

1. Check that _Preferences → SEO and URLs → Enable friendly URLs_ is set to YES
2. Save this page (regenerates the `.htaccess`)
3. Clear the PrestaShop cache twice
4. Go to _Modules → Positions_, verify that `dffacetedseo` is attached to `moduleRoutes`
5. Test fallback: `https://your-site/index.php?fc=module&module=dffacetedseo&controller=landing&slug=your-slug`

### The search engine returns no products

The index is probably empty. Go to the dashboard and click **Rebuild index**.

### Filters do not show up in the column

Verify in _Modules → Positions_ that `dffacetedseo` is attached to the `displayLeftColumn` hook. Depending on your theme, you can also attach it to `displayRightColumn` or `displayHome`.

### Degraded performance after mass product import

Incremental invalidation works well for single modifications. After a mass import, it is faster to rebuild the entire index from the dashboard and clear the cache.

## Updates

The module follows `MAJOR.MINOR.PATCH` versioning. Critical fixes (security, fatal errors) ship as patches; new features trigger a minor bump. Every major update ships an `upgrade/upgrade-X.Y.Z.php` script that runs automatically when installing the new version.

## Support

For any question or bug report, contact [support@datafirefly.com](mailto:support@datafirefly.com) with the PrestaShop version, PHP version, module version and PHP/PrestaShop logs.
