# SEO Cannibalization Detector — Complete guide for PrestaShop 8 & 9

> SEO Cannibalization Detector connects in read-only mode to your Google Search Console property, identifies every query where multiple URLs of your PrestaShop store cannibalize each other, and proposes a concrete…

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

SEO Cannibalization Detector connects in read-only mode to your Google Search Console property, identifies every query where multiple URLs of your PrestaShop store cannibalize each other, and proposes a concrete action: consolidate, redirect 301, differentiate or monitor. This documentation covers installation, Google service account setup, launching your first scan, reading the report and managing redirects.

## Prerequisites

- PrestaShop 8.0 to 9.x
- PHP 8.1 or higher, with `openssl`, `curl` and `json` extensions
- A Google Cloud project (free account is enough, no credit required)
- A Google Search Console property already verified for your store's domain

**Check OpenSSL:** on most hosts (o2switch, OVH, Hetzner, Infomaniak), OpenSSL is enabled by default. To verify, temporarily add `echo extension_loaded('openssl') ? 'OK' : 'KO';` to a test PHP file.

## Installation

1. Download the `dfcannibalization-1.0.0.zip` file from your DataFirefly customer area.
2. In the PrestaShop back office, go to **Modules → Module Manager**.
3. Click **Upload a module** and select the ZIP.
4. Once installation is complete, click **Configure**.

The module creates 4 SQL tables (`scan`, `query`, `page`, `action`), an admin tab "DataFirefly Cannibalization" under IMPROVE, and a unique token for scheduled scans.

## Create a Google service account

A service account is a Google technical account that lets your store read Search Console data without interactive authentication. It is free and limited to read-only.

### 1. Enable the Search Console API

1. Open [console.cloud.google.com](https://console.cloud.google.com/).
2. Create a new project (e.g. "DataFirefly SEO") or select an existing project.
3. In the navigation menu, go to **APIs & Services → Library**.
4. Search for _Google Search Console API_ and click **Enable**.

### 2. Create the service account

1. In the same menu, go to **APIs & Services → Credentials**.
2. Click **Create Credentials → Service Account**.
3. Give it an explicit name (e.g. "datafirefly-cannibalization") and confirm. You can skip the optional roles and user access steps.

### 3. Generate the JSON key

1. Click the created service account.
2. **Keys** tab → **Add Key → Create new key**.
3. Select **JSON** format and confirm. The file downloads automatically.

**Keep this file in a safe place:** it contains the service account's private key. Anyone with this JSON can read your Search Console data.

## Authorize the service account in Search Console

The service account has its own email (visible in the JSON under the `client_email` key, e.g. `datafirefly-cannibalization@project-123.iam.gserviceaccount.com`). It must be granted read access on your property.

1. Open [search.google.com/search-console](https://search.google.com/search-console).
2. Select your property (URL-prefix or domain).
3. Click **Settings → Users and permissions**.
4. Click **Add user**.
5. Paste the service account's `client_email` and choose the **Restricted** permission (read-only).
6. Confirm.

## Configure the module

Return to the PrestaShop back office, in the module configuration.

### Service account and property

- **Service account JSON**: open the downloaded file, copy-paste its full contents into the field.
- **Site URL (GSC property)**: reproduce exactly the format displayed in Search Console.
   - For a URL-prefix property: `https://www.example.com/` (with trailing slash).
   - For a domain property: `sc-domain:example.com` (no protocol, with `sc-domain:` prefix).

### Detection thresholds

- **Lookback period**: 7 to 490 days. 90 days is a good compromise between freshness and statistical volume.
- **Minimum impressions**: ignore very low queries. Default 30. Set to 10 if your store is recent.
- **Minimum clicks**: click threshold per query to be analyzed. Default 1. Set to 0 to also include impression-only queries (useful for young sites).
- **Maximum position**: ignore queries where the best URL is beyond this position. Default 30.

### Automatic 301 redirects

Enables interception via the PrestaShop dispatcher hook. Disable if you prefer to manage redirects manually in _Traffic and SEO → Redirects_.

Click **Save**, then **Test GSC connection**. If the connection succeeds, the module shows a green message.

**"403 User does not have sufficient permissions":** the service account has not yet been added to Search Console, or the property entered does not match exactly the one displayed in GSC. Check both sides.

## Run a scan

Click **Run scan now**. The module:

1. Queries the Search Console API for the configured period (pagination up to 200,000 rows).
2. Groups rows by query.
3. Filters according to your thresholds (impressions, clicks, position).
4. Keeps only queries with at least 2 distinct URLs.
5. Computes the severity score and the recommendation.
6. Persists everything to the database.

For an average site (10,000 to 50,000 pages), a full 90-day scan takes between 30 seconds and 3 minutes.

## Read the report

Once the scan is complete, open it from the **Scan history** tab. The report lists each cannibalized query with:

- **The keyword** (the Google query).
- **The severity score** as a colored badge (low / medium / high / critical).
- **The number of competing URLs.**
- **The recommendation** (REDIRECT 301, CONSOLIDATE, DIFFERENTIATE, MONITOR).
- **The status** (pending, reviewed, resolved, ignored).

Click on a query to see the details: the list of competing URLs with clicks, impressions, average position, CTR, and page type (product, category, CMS, blog, etc.). The URL identified as "winner" is highlighted in green.

## Understanding the severity score

The score is out of 100, combining 4 weighted factors:

- **Number of competing pages (25 points max)** — formula: `min((n-1)/3, 1) × 25`. From 4 competing URLs onwards, the factor is maximal.
- **Click distribution (30 points max)** — based on an inverted HHI index. 50/50 cannibalization between two URLs is worse than 95/5. Formula: `((1-HHI)/(1-1/n)) × 30`.
- **Position gap (30 points max)** — weighted by the best result's position. Multiplier × 1.0 if best position ≤ 10, × 0.7 if ≤ 20, × 0.4 beyond.
- **Impression volume (15 points max)** — logarithmic scale: `min(log10(impr+1)/4, 1) × 15`.

Levels: **low** < 26, **medium** < 51, **high** < 76, **critical** ≥ 76.

## The 4 recommendations

### REDIRECT 301

The module proposes this action when one URL captures at least 70% of clicks at average position ≤ 15. This is the "winning" URL: Google clearly considers it the best answer. The other URLs are redirected to it via 301. Content can be merged editorially before the redirect if needed.

### CONSOLIDATE

Performance is split (no clear winner). Recommendation: merge the content of the two URLs into a single one, then 301-redirect the other. This work is editorial, not automatable. The module identifies the case but does not perform the merge for you.

### DIFFERENTIATE

Pages are of different types (example: a product sheet and a blog article). Cannibalization indicates a title/meta tag targeting problem rather than a real content conflict. Recommendation: rework the tags to target distinct queries (long-tail on the blog, main query on the product).

### MONITOR

Score is low (< 26). No urgent action. The module keeps the query in the report but flags it "to watch".

## Apply a 301 redirect

From the detail of a query recommended REDIRECT 301:

1. Click the **Select as source** button next to the URL to be redirected (the one that is not the winner).
2. The target URL (the winner) is pre-filled automatically.
3. Click **Apply redirect**.

The rule is saved and served immediately from the `actionDispatcherBefore` hook. Performance: a single SELECT per PHP-FPM worker thanks to a static in-memory cache.

**Verify a redirect works:** open the source URL in a browser in private mode, or use `curl -I https://your-store.com/old-url`. You should see an `HTTP/1.1 301` and a `Location:` header pointing to the new URL.

## Manage active redirects

The **Redirects 301** tab lists all redirects in place with their hit counter. You can:

- **Disable** a redirect without deleting it (useful to test a rollback).
- **Delete** a redirect permanently.
- **See the hit count** since creation (measures effectiveness).

## Scheduled scans via cron

The module exposes a token-protected front controller. The URL is shown in the module configuration as:

```
https://your-store.com/index.php?fc=module&module=dfcannibalization&controller=cron&token=XXXX
```

### Standard Linux cron

On a Linux/Unix server with SSH access, edit your crontab:

```
crontab -e
```

For example, add a scan every Sunday at 11 PM:

```
0 23 * * 0 curl -s "https://your-store.com/index.php?fc=module&module=dfcannibalization&controller=cron&token=XXXX" > /dev/null
```

### Scheduled tasks OVH / o2switch / Infomaniak

All these hosts offer a scheduler in their back office. Enter the full URL and the desired frequency. For most stores, a weekly scan is more than enough.

**Regenerate the token:** uninstall then reinstall the module if you think your token has leaked. A new token will be generated at install.

## Multi-shop

All tables include an `id_shop` column. Scans are scoped to the active shop in the back office selector. Each shop has its own configuration (service account, GSC URL, thresholds) and its own scan history.

If you manage multiple domains from a single back office, create a distinct service account per property, or authorize the same service account on all your Search Console properties.

## Troubleshooting

### "OpenSSL extension required"

Ask your host to enable the PHP OpenSSL extension. On 99% of modern hosts, it is enabled by default.

### "Invalid service account JSON"

The pasted JSON is incomplete or corrupted. Re-download the key from Google Cloud Console and paste it whole, unmodified.

### "Site URL must match exactly the GSC property"

Check the format in Search Console. A URL-prefix property is written `https://www.example.com/` (trailing slash). A domain property is written `sc-domain:example.com` (no protocol, no slash).

### "403 User does not have sufficient permissions"

The service account has not been authorized in Search Console, or has been authorized on another property. Check Settings → Users and permissions.

### The scan runs but finds no cannibalization

Your thresholds might be too high for your volume. Try lowering minimum impressions to 5 and minimum clicks to 0.

### A 301 redirect does not trigger

Check that (1) the "Automatic 301 redirects" option is enabled in the configuration, (2) the rule is marked as active in the Redirects 301 tab, (3) the PrestaShop cache has been cleared after activating the module.

## Going further

- **Review policy**: on critical queries, intervene within 7 days. On high, within 30 days. On medium, as part of a quarterly review.
- **301 strategy versus consolidation**: the 301 redirect transfers PageRank but loses the ability to have two indexed pages. Prefer differentiation for distinct search intents.
- **Product sheet edge case**: if two very close variants cannibalize each other, merge them into a product with variants rather than two separate products.
