SEO Link Obfuscation for PrestaShop
Installation, preset configuration, writing CSS selectors and verifying what crawlers actually receive.
This guide covers installation, configuration and verification of the dfobfuscator module on PrestaShop 8 and 9.
Installation
- Go to Modules and Services, Upload a module in your back office.
- Drop the
dfobfuscator.zipfile and confirm. - Click Configure once installation completes.
The module registers itself on two hooks: actionOutputHTMLBefore for HTML rewriting and actionFrontControllerSetMedia for loading front-end assets. No core file override is created.
The PHP DOM extension must be enabled. It is on by default on nearly all hosting. Without it, the module stays installed but leaves the HTML unchanged.
Configuration
Enable obfuscation
The main switch disables all rewriting without uninstalling the module. Useful to compare source code before and after, or to isolate the module during diagnostics.
The three presets
| Preset | What it targets | Default |
|---|---|---|
| Faceted navigation | Filter blocks and the active filter list | On |
| Product sorting | The sort dropdown | On |
| Pagination | Links to page 2, 3 and beyond | Off |
Only enable pagination if your XML sitemap and internal linking make every product page reachable another way. Otherwise you cut the only crawl path to part of your catalog.
Custom CSS selectors
One selector per line. Commas on a single line are also accepted. Every link matching a selector is obfuscated.
#footer .account-links a
.block-tags a
.my-custom-block a[data-filter]
The built-in converter supports tag, id, class, attributes (presence or exact value), the descendant combinator and the direct child combinator. If your shop ships symfony/css-selector in its vendor directory, the module uses it first and you get the full CSS syntax.
XPath lines
For selections CSS cannot express, prefix the line with xpath::
xpath://div[@id='js-product-list']//a[contains(@href,'?order=')]
Excluded controllers
A comma-separated list of front controller names. On those pages no rewriting happens and the assets are not loaded. Default value: order,cart. Add any controller whose flow must not depend on JavaScript.
How the rewriting works
On the actionOutputHTMLBefore hook, the module receives the complete page HTML. It first checks that this is a full HTML document, which rules out AJAX and JSON responses. It then loads the document into DOMDocument, converts each selector into an XPath query, and replaces the matched links with span elements.
Each obfuscated element receives:
- a
data-oattribute holding the URL in reversed base64; - the
df-obfclass alongside its original classes; tabindex="0"androle="link"for accessibility;data-twhen the original link carried a target attribute.
Links whose URL starts with #, mailto:, tel: or javascript: are skipped. The rel attribute is stripped, the target attribute is kept as data-t.
Automatic fallback
If document parsing fails, or if no selector matches a link, the original HTML is returned unmodified. The module therefore cannot break a page because of unexpected markup.
AJAX-loaded content
Filter refreshes and infinite scroll reinject links after the initial render. The front.js file installs a MutationObserver that applies the same selectors to those new links, browser side. Selectors are passed to the front end through Media::addJsDef. XPath lines are excluded from this mechanism, since the browser does not evaluate them.
Verifying that it works
- Open a category page with filters, then view the page source (Ctrl+U, not the element inspector).
- Search for
df-obf: you should find span elements where the filter links used to be. - Search for a filter URL, for example
?q=: it should no longer appear in an href attribute.
The element inspector shows the DOM after JavaScript execution, not the served HTML. To judge the result from a crawler’s point of view, always use view source or a tool such as curl.
On the navigation side, test left click, middle click, ctrl+click (cmd+click on macOS) and the Enter key after reaching a filter by tabbing.
Troubleshooting
Nothing is obfuscated
Check in order: the main switch is on, at least one preset or selector is set, the current controller is not in the exclusion list, the PHP DOM extension is available. Then clear the PrestaShop cache.
A custom theme is not covered
Presets target Classic theme selectors. On a bespoke theme, inspect the filter block to note its container id or class, then add a custom selector line.
A link is obfuscated by mistake
Narrow the selector scope by targeting a more specific container, or remove the preset involved and replace it with tighter custom selectors.
An AJAX search module loses its links
Some third-party modules rebuild their filter block entirely. If behaviour differs between the first display and a refresh, check that your custom selector applies to the rebuilt container rather than to an element destroyed on every update.
Frequently asked questions
Is this cloaking?
No. The served HTML is identical for everyone: no user-agent test, no page variant. The link exists for nobody in the source code, and the browser reconstructs it at interaction time.
Do already indexed URLs disappear?
No. Obfuscation acts on the discovery of new URLs. For URLs already in the index, keep your noindex directives or redirects until Google revisits them.
What is the performance impact?
A DOM parse of the final page, in the range of a few milliseconds on a standard category page. On the front end, 5 KB of JavaScript and 0.2 KB of CSS.
Should I disable the module during an SEO audit?
No, quite the opposite: the audit should reflect what crawlers see. If your audit tool reports a drop in internal link count on category pages, that is the expected result.