PrestaShop URL structure: clean URLs, canonicals and avoiding duplicate content
Last updated: June 2026.
Your PrestaShop store's URL structure is the one SEO decision that quietly shapes everything else. It decides how many copies of each product Google can find, how much crawl budget gets wasted on duplicates, and, the day you change it, whether years of rankings survive or evaporate. PrestaShop is unusually prone to one specific URL problem: by default it bakes the category path into product URLs, so a single product that lives in two categories becomes two URLs for the same thing. That is duplicate content the platform creates for you, automatically, and it is the core thing this guide is about: getting to a clean, single URL per page, and migrating to it without burning your traffic to the ground.
This is the practical version, the back-office settings, the database columns, the redirect mechanics, not the textbook "what makes a good URL." If you only want to know what friendly URLs are and why to turn them on, that's a smaller question answered in what are friendly URLs and why every PrestaShop store needs them. This post assumes you've already enabled them and now need the URL structure underneath to be right.
Where PrestaShop's URL behaviour actually lives
Before changing anything, know which switches you're dealing with. PrestaShop's URL behaviour is controlled from Shop Parameters → Traffic & SEO → Set up URLs (in 1.6 it was Preferences → SEO & URLs). The settings that matter for structure:
- Friendly URL (
PS_REWRITING_SETTINGS). Turns?id_product=42&controller=productinto a readable slug. This requires URL rewriting (Apachemod_rewriteor the equivalent Nginx rules) and writes the rewrite block into your.htaccess. - Accented URL (
PS_ALLOW_ACCENTED_CHARS_URL). Leave this off unless you have a deliberate reason; ASCII slugs are safer across crawlers and shares. - Redirect to the canonical URL (
PS_CANONICAL_REDIRECT), when set to 301 Moved Permanently, PrestaShop itself redirects a request that doesn't match the page's canonical slug to the correct one. This is your first line of defence against duplicate URLs and is often left on the weaker 302 or No redirection setting by accident.
The actual slug for each page is stored as link_rewrite in the database. ps_product_lang, ps_category_lang and ps_cms_lang each have a link_rewrite column, one row per language. That's the value PrestaShop assembles your URL from. Knowing this matters because a "URL change" in PrestaShop is really a change to these columns plus the route pattern, not a free-text rewrite, and anything you fix in the back office ultimately lands here.
For a quick duplicate-slug audit, start with the language-aware product table. Adjust the ps_ prefix if your shop uses a different database prefix:
SELECT id_lang, link_rewrite, COUNT(*) AS products
FROM ps_product_lang
GROUP BY id_lang, link_rewrite
HAVING COUNT(*) > 1
ORDER BY products DESC, link_rewrite;
The duplicate-content problem PrestaShop builds in

Here is the specific structural flaw. Native PrestaShop product, category and CMS routes normally include ID tokens, and the product route often also includes the category path. So a product assigned to Bags and to Sale can be reachable through more than one category-flavoured address, with the product ID still present in the route. Same product, same content, multiple addresses.
One version-specific note before you act on this: the category-in-product-URL behaviour above is the default on PrestaShop 1.7 and 8. PrestaShop 9 removes the category name from product URLs by default, so a fresh PS9 store already sits closer to the flat, single-URL-per-product structure this guide is steering you toward, and it excludes filtered/faceted listing pages from indexing by default too. Check which behaviour your store actually has (view a product that lives in two categories and see whether the category appears in its address) before planning a migration you may not need.
PrestaShop tries to paper over this by emitting a canonical tag pointing at the product's "default category" path and, if PS_CANONICAL_REDIRECT is set to 301, redirecting the others. In practice we still see Google index the non-canonical variants on plenty of stores. Internal links, old sitemaps and external links keep feeding the alternates, and ranking signals get split across copies that should be one. So what does that cost you? A product that could rank on the strength of all its links instead competes with itself, and your crawl budget is spent re-fetching duplicates instead of discovering new pages.
The clean fix is to remove the category dependency from product URLs and, if you want ID-free paths, use a friendly-URL module that provides routing for {rewrite}-only URLs:
| Schema | Example URL | Duplicate risk |
|---|---|---|
| Native product route with category and ID tokens | /bags/42-blue-leather-wallet | High. Category context can create alternates |
| Native route without category, keeping the ID | /42-blue-leather-wallet | Lower, one product URL, but still ID-based |
Friendly-URL module route: {rewrite} | /blue-leather-wallet | Lower, clean, ID-free URL handled by module routing |
A flat product URL gives each product exactly one address no matter how many categories it belongs to. Categories themselves still benefit from shallow hierarchy (/bags/leather-bags tells users and Google where they are). Just keep it to about two levels and don't nest products under it. ID-free routing is module work, not a native PrestaShop route edit; our Smart SEO Friendly URL Manager handles custom URL patterns, overrides, collision handling and redirects from the back office, so you change the structure without hand-editing route patterns or risking a typo that 500s the catalogue.
The smaller duplicate-content sources, in one place
Category paths are the big one, but four cheaper duplications are worth closing in the same pass. Each is a one-line server fix:
- www vs non-www, if both
www.and the bare domain resolve, every page exists twice. Pick one, 301 the other in.htaccess(PrestaShop also exposes a Redirect to the canonical URL/domain setting under Traffic & SEO). - HTTP vs HTTPS, same problem; force HTTPS with a 301 and enable PrestaShop's Enable SSL on all pages.
- Trailing slash.
/walletsand/wallets/are different URLs. PrestaShop's default is no trailing slash; keep it and redirect the slashed variant. - Tracking parameters,
?utm_source=…creates infinite variants. A correct self-referencing canonical neutralises them; just confirm PrestaShop isn't echoing the parameters into the canonical tag itself.
The canonical tag is the tool doing most of the quiet work in that list, and it deserves more than a footnote, how it's generated, when to override PrestaShop's default, and the paginated-page edge cases all live in canonical URLs: one simple tag that prevents duplicate content problems. For fine-grained control over what PrestaShop puts in the canonical on product pages, our Product Canonical Manager lets you set it per product instead of accepting the default-category logic.
When you should, and shouldn't, change URLs
Changing URL structure carries real risk, so the first question is whether you need to. Legitimate reasons: category paths splitting products across duplicate URLs, an upgrade from 1.6 to 1.7/8/9 that shifted your patterns, or a catalogue restructure that already moved your category URLs. Cosmetic reasons, "the IDs look ugly," "I'd prefer no .html", usually are not worth it.
The rule we hold to: a URL that ranks is worth more than a prettier URL that starts from zero. If a product sitting at /42-blue-wallet ranks on page one, removing that ID buys you nothing and risks the position. Only restructure where the current structure is actively causing duplication or waste.
What a URL migration actually costs when it goes wrong
We need to be blunt, because we've cleaned up enough of these: a URL change without a complete redirect plan reliably causes a real, immediate traffic drop while Google re-crawls and re-evaluates every changed address, and pages can fall out of the index during that window. The damage comes from four places:
- Lost backlinks, every external link to an old URL hits a 404 unless redirected, and the equity those links carried transfers to nothing.
- Broken internal links, menus, footers, cross-sells, CMS pages and email templates contain hardcoded URLs that simply break.
- Wasted crawl budget, if old URLs still return 200 alongside the new ones, Google crawls both, doubling demand for zero gain.
- Recovery time measured in weeks, not days. Even a flawless migration takes Google time to reprocess; industry write-ups typically describe well-executed migrations recovering over a couple of months, but treat any specific percentage as directional and watch your own data.
None of that is a reason to avoid a necessary migration. It's the reason to do the next four steps in order.
Step 1, Map every URL before you touch anything
You cannot redirect what you haven't listed. Build one spreadsheet that becomes your migration map, with a row for every URL that has ever been indexed, linked to, or received traffic:
- Crawl the live site with Screaming Frog, Sitebulb or similar and export every URL. Flag the PrestaShop-specific patterns: products reachable via multiple category paths, ID-prefixed URLs, parameter URLs (
?id_product=…&controller=product), and deep pagination (?page=2…50). - Export your ranking URLs from Search Console → Performance → Pages over the full 16 months. These are the pages Google actively values: every one needs a destination.
- Export backlink data (Ahrefs, Semrush, or GSC's Links report) so you know which old URLs carry external equity. Those are your highest-priority redirects.
Columns to keep: current URL, HTTP status, current canonical, backlink count, monthly organic clicks, and the target new URL. That last column is the work of Step 2 and 3.
Step 2, Design the new structure
For PrestaShop specifically, the structure decisions reduce to a short list:
- Flatten product URLs, with a friendly-URL module, use a
{rewrite}-style product pattern so products sit at the root and stop duplicating across categories. This is the single highest-value change for most stores that need ID-free URLs. - Keep category hierarchy shallow, two levels, lowercase, hyphen-separated.
- Drop IDs,
.htmland language prefixes you don't need, but only on URLs that aren't already ranking (see the rule above). A single-language store gains nothing from/en/in every path. - Hyphens, always lowercase, Google reads
blue-walletas two words andblue_walletas one; URLs are case-sensitive, so/Blue-Walletand/blue-walletare two different pages. Yourlink_rewritevalues should be lowercase, hyphenated, and free of accents and special characters.
Step 3, Build a one-to-one redirect map
This is the step that decides whether the migration survives. The golden rule: every old URL 301s to its direct equivalent, the same content at its new address, never a blanket redirect to the homepage. Mapping everything to the homepage is read by Google as a soft 404; the old URL's ranking signals are simply discarded.
/42-wallets → /wallets
/bags/leather-bags/blue-wallet → /blue-wallet
/123-blue-wallet.html → /blue-wallet
For pages with no equivalent, a discontinued product, work down this order: redirect to the closest replacement product; failing that, to the parent category; and only if neither exists, return a 410 Gone (which tells Google to drop the URL faster than a 404).
Avoid redirect chains. A → B → C counts as multiple crawled URLs for one piece of content, adds latency on every hop, and bleeds a little equity at each step. If you carry redirects from a previous migration, repoint them straight to the final destination:
Bad: /old-url → /intermediate → /final
Good: /old-url → /final
Step 4, Implement the redirects in PrestaShop
Three realistic routes, suited to different stores:
| Method | Where it runs | Best when… | Watch out for |
|---|---|---|---|
| .htaccess (Apache) | Web server, before PrestaShop loads | You're comfortable with regex and want the fastest execution | One bad pattern can take the whole site down; place rules before PrestaShop's rewrite block |
| Redirect module | PrestaShop / database | Most stores, non-technical staff manage it, CSV bulk import, 404 auto-detection | Slight PHP overhead vs server-level rules |
| Nginx config | Server, loaded in memory | Docker / Nginx deployments | Needs nginx -s reload; lives outside the back office |
For an .htaccess approach, pattern rules cover the bulk changes cleanly:
# remove .html extension
RewriteRule ^(.+)\.html$ /$1 [R=301,L]
# strip leading ID prefix
RewriteRule ^[0-9]+-(.+)$ /$1 [R=301,L]
# drop /en/ on a single-language store
RewriteRule ^en/(.*)$ /$1 [R=301,L]
[R=301,L] means "return a permanent redirect and stop processing further rules." For most merchants the module route is safer: a database-driven redirect manager gives you CSV import for thousands of rows, automatic 404 capture with suggested targets, and no chance of a regex typo breaking the storefront.
Step 5, Update everything that still points at the old URLs
Redirects are a safety net, not a substitute. Every internal reference should point straight at the new URL, or you waste crawl budget and add latency on your own navigation. Sweep:
- Navigation menus, footer, sidebar links
- CMS page content and any hardcoded product/category links inside product descriptions
- Email templates, order confirmation, shipping, abandoned-cart
- Google Merchant Center feed and JSON-LD structured data (both embed product URLs)
- The database itself, confirm the
link_rewritevalues inps_product_lang,ps_category_langandps_cms_langare clean for every language, not just the default - Your XML sitemap. Regenerate it with new URLs only; the mechanics are in the complete guide to XML sitemaps for PrestaShop SEO
Multi-language stores: the redirect count multiplies
Every product exists once per language, /en/blue-wallet, /de/blaue-brieftasche, /fr/portefeuille-bleu, so a structure change you make in English needs a matching redirect in every other language. Keep the language prefixes (they tell Google which version to serve), make sure your hreflang tags point at the new URLs across all versions, and update link_rewrite for all languages. A 5,000-product catalogue in four languages can need on the order of 20,000 redirects for a full restructure. Which is exactly why the spreadsheet in Step 1 has to be complete before you start.
After you deploy: verify, then watch
The first 72 hours are mechanical, not strategic. Spot-check 50–100 old URLs and confirm each returns a single 301 to the right destination, scan your server logs for any 404 spike that signals a missed mapping, and resubmit the new sitemap. After that, the migration becomes a Search Console monitoring job, the week-by-week reading of the Performance and Pages reports, what a normal dip looks like versus a real problem, and when "Crawled. Currently not indexed" means something, and that workflow is its own discipline, covered in Google Search Console for PrestaShop: setup, monitoring and fixing errors. Keep redirects with backlinks in place permanently; the rest can retire after a year or so once external sites have updated their links.
The honest summary
A PrestaShop URL restructure is a project, audit, plan, redirect map, implementation, internal-link sweep, sitemap regeneration, weeks of monitoring, not a Friday-afternoon toggle. But when category paths are splitting your products across duplicate URLs and crawl budget is draining into parameter variations, the flatter, single-URL-per-page structure pays for itself in consolidated ranking signals and cleaner crawling. Plan it properly, redirect everything one-to-one, fix the internal references, and give Google a couple of months to settle. A well-executed migration recovers. A rushed one is the kind of damage we get hired to undo. For the wider SEO picture this URL work sits inside, start from the complete guide to ranking higher on PrestaShop.
FAQ
Should I use a 301 or a 302 redirect when I move a URL?
For a permanent URL change, which is what a structure migration is, use 301. A 301 ("Moved Permanently") tells Google to transfer the old URL's ranking signals to the new one and update its index. A 302 ("Found"/temporary) tells Google to keep the old URL indexed and not pass the equity, so using it for a permanent move leaves your rankings stranded on the old address. The only place 302 belongs is a genuinely temporary redirect, which a URL restructure isn't. PrestaShop's own PS_CANONICAL_REDIRECT setting should likewise be on 301, not 302.
Will I lose rankings when I restructure my URLs?
Expect a temporary dip while Google re-crawls and re-evaluates every changed address, that's normal even on a flawless migration. What turns a dip into lasting damage is missing redirects: every old URL must 301 to its direct equivalent, never to the homepage. Do the one-to-one redirect map, fix internal links, regenerate the sitemap, and a well-executed migration typically recovers over a couple of months. Treat any specific recovery percentage you read as directional and watch your own Search Console data.
Do I have to remove the numeric ID from my product URLs?
No, and often you shouldn't. The ID prefix is cosmetic; it doesn't hurt rankings. The rule we hold to is that a URL that ranks is worth more than a prettier one that starts from zero. Strip IDs only where the current structure is actively causing duplication (category paths splitting one product across several URLs), and remember ID-free routing is module work, not a native route edit, so it carries redirect risk you have to plan for. If a product at /42-blue-wallet ranks on page one, leave it.
.htaccess redirects or a redirect module. Which should I use?
For most stores, the database-driven redirect module is safer: non-technical staff can manage it, it imports thousands of rows from CSV, and it captures 404s automatically with suggested targets. With no regex typo that can take the whole storefront down. Choose .htaccess rules only when you're comfortable with regex and want server-level speed, and always place your rules before PrestaShop's own rewrite block. On Docker/Nginx deployments the redirect lives in the Nginx config and needs a reload to take effect.
Does PrestaShop 9 change the duplicate-URL problem this guide describes?
Yes, for the better. PrestaShop 9 removes the category name from product URLs by default, which is the single biggest source of the multi-category duplication this guide is about, and it keeps filtered/faceted listing pages out of the index by default. A fresh PS9 store therefore needs less of the restructuring work here than a 1.7 or 8 store does. If you're upgrading to PS9 from an older version, note that a major upgrade can itself shift your URL patterns. Which is a migration, with all the redirect work in Steps 1 through 5 above.