Every PrestaShop fix we catalog
SEOLZ catalogs 124 fixes for PrestaShop across 5 areas — SEO, answer-engine readiness, accessibility, security and site-health. Each lists the exact steps for PrestaShop, with a link to the official docs.
SEO · 44 fixes
Cwv inpLarger project
Reduce Interaction to Next Paint (INP) to under 200 ms by breaking up long JavaScript tasks, deferring non-critical third-party scripts, and lightening event handlers so the page feels fast and responsive after every tap or click.
On PrestaShop
- Go to Back Office → Advanced Parameters → Performance. Enable 'Compress, minify and cache JavaScript' and 'Combine JavaScript files'.
- In Modules → Module Manager, disable or uninstall any front-end module (chat, slider, pop-up) that you are not actively using — each adds JavaScript weight.
- In Back Office → Design → Theme & Logo → Advanced Customisation, identify custom JS files added to the theme. Open them and refactor heavy event handlers to defer work using `setTimeout`.
- Use PageSpeed Insights to identify which specific interactions are slow, then target those handlers in the theme's JavaScript.
Cwv lcpModerate effort
Reduce Largest Contentful Paint (LCP) to under 2.5 seconds by serving the hero image in a next-gen format (WebP/AVIF), preloading it, and eliminating render-blocking resources.
On PrestaShop
- Convert your hero/slider images to WebP and upload them via Back Office → Design → Image Slider (or your installed slider module, e.g., ps_imageslider).
- Install the 'psgdpr' or a dedicated performance module like 'PrestaSEO Speed' or 'Krona SEO & Speed' from the PrestaShop Addons marketplace to handle JS/CSS minification and deferral.
- Edit your theme's header.tpl (themes/YourTheme/templates/_partials/head.tpl) to add: <link rel='preload' as='image' href='{$hero_image_url}' fetchpriority='high'>.
- In the slider module settings, ensure the first slide image does NOT have lazy loading enabled.
- Enable PrestaShop's built-in CCC (Combine, Compress, Cache) settings: Back Office → Shop Parameters → General → CCC → enable for CSS and JS.
- Clear the cache (Advanced Parameters → Performance → Clear cache) and verify with PageSpeed Insights.
<!-- Step 1: Preload the LCP hero image -->
<link
rel="preload"
as="image"
href="/images/hero-banner.webp"
imagesrcset="/images/hero-banner-400.webp 400w,
/images/hero-banner-800.webp 800w,
/images/hero-banner-1500.webp 1500w"
imagesizes="100vw"
fetchpriority="high"
>
<!-- Step 2: Hero <img> tag — eager, high priority, no lazy-load -->
<img
src="/images/hero-banner-1500.webp"
srcset="/images/hero-banner-400.webp 400w,
/images/hero-banner-800.webp 800w,
/images/hero-banner-1500.webp 1500w"
sizes="100vw"
alt="[Descriptive alt text for your hero image]"
width="1500"
height="600"
loading="eager"
fetchpriority="high"
>Add inside <head>, immediately after <meta charset>. Replace the href with your actual hero image CDN URL.
Duplicate meta descriptionModerate effort
Write a unique, page-specific meta description for every page on your store so Google can display a compelling, accurate snippet for each one in search results.
On PrestaShop
- For a product: go to Catalog → Products, open the product, click the 'SEO' tab, and fill in the 'Meta description' field.
- For a category: go to Catalog → Categories, click 'Edit' on the category, select the 'SEO' tab, and update 'Meta description'.
- For CMS pages: go to Design → Pages, open the page, update the 'Meta description' field in the SEO section.
- For the homepage: go to Shop Parameters → Traffic & SEO → SEO & URLs, find the 'index' page entry, and edit its meta description.
- For bulk updates, use an SEO module such as 'SEO Expert' from the PrestaShop Marketplace, which provides a spreadsheet-style editor for all meta descriptions.
- Clear the cache (Advanced Parameters → Performance → Clear cache) after making changes, then verify with a browser and re-index in Google Search Console.
<head>
<meta charset="UTF-8">
<meta name="description" content="Shop women's running shoes at Acme Store — lightweight, cushioned designs for road and trail. Free shipping on orders over $50.">
<title>Women's Running Shoes | Acme Store</title>
</head>Place inside the <head> element of every page. Replace the content value with a unique 120–160 character description for that specific page.
Duplicate titleModerate effort
Give every page a unique, descriptive title tag that accurately reflects that page's specific content.
On PrestaShop
- For products: go to Catalog → Products → open a product → 'SEO' tab → edit the 'Meta title' field.
- For categories: Catalog → Categories → open a category → 'SEO' tab → 'Meta title'.
- For CMS pages: Design → Pages → open a page → edit 'Meta title'.
- For the shop's default title settings: Shop Parameters → General → set 'Shop name' which is appended to page titles; ensure templates use {$meta_title} in the head.tpl Smarty template.
- For bulk updates use a CSV import module or an SEO module (e.g. SEO Expert by PrestaHero) that lets you set title templates with dynamic product/category name variables.
<head>
<title>Merino Wool Running Socks – Free UK Shipping | BrandName</title>
</head>Place inside <head>. Make the title unique per page — include the specific item name and brand.
Faceted url indexableModerate effort
Point the rel=canonical on every faceted/filter URL to its clean parent category URL (or add noindex), so Google treats filtered pages as a single authoritative page instead of thousands of duplicates.
On PrestaShop
- PrestaShop layered navigation (the 'ps_facetedsearch' module) generates URLs like /womens-pants#/color-navy/size-s or query-string variants depending on your URL config.
- Go to Modules → Module Manager → search for 'Faceted Search' (ps_facetedsearch) → Configure. Look for options related to 'SEO', 'Indexing', or 'Canonical URL'. Recent versions of this module include options to set a canonical to the parent category.
- If the module doesn't offer this natively, go to Modules → Module Manager → install an SEO module such as 'SEO Expert' or 'SEO Manager' that allows you to set canonical rules for layered navigation pages.
- Alternatively, edit the module's template: in /modules/ps_facetedsearch/views/templates/front/, find the layout or head template and add canonical logic that strips filter parameters and outputs the clean category URL.
- In Back Office → Shop Parameters → Traffic & SEO → SEO & URLs, verify that faceted search URLs are not being added to your sitemap.
- Verify with Google Search Console URL Inspection.
<head>
<!-- Canonical points to the CLEAN category URL, not the filtered variant -->
<link rel="canonical" href="https://www.example.com/womens-pants">
</head>Place this in the <head> of every filtered/faceted page. The href must be the clean parent category URL — never the filtered URL itself.
Images missing altModerate effort
Add descriptive alt text to every product and content image so search engines and screen readers can understand what the image shows.
On PrestaShop
- FOR PRODUCT IMAGES: Go to Catalog > Products, open a product, click the 'Images' tab, click on an image, and fill in the 'Caption' field for each language (this outputs as the alt attribute). Save.
- FOR CMS PAGE IMAGES: Go to Design > Pages, open a CMS page, click the image in the TinyMCE/rich-text editor, then click 'Edit image' and complete the 'Image description (alt text)' field.
<!-- BEFORE (missing alt — bad) -->
<img src="/images/red-leather-bag.jpg">
<!-- AFTER (descriptive alt — correct) -->
<img src="/images/red-leather-bag.jpg" alt="Red leather crossbody bag with gold buckle clasp">
<!-- Decorative image (intentionally empty alt — correct) -->
<img src="/images/divider-line.png" alt="">Place this wherever an <img> tag appears in your HTML. Replace the placeholder text with your real description.
Images missing alt bulkModerate effort
Add descriptive alt text to every product and content image at the template level so search engines and screen readers can understand all images on the page.
On PrestaShop
- Go to Catalog → Products → edit a product → Images tab. Click each image; you will see language-specific 'Caption' fields — fill these in as they serve as the alt text.
- For the product listing (category pages), the alt text is pulled from the image caption automatically by the default theme. If your custom theme overrides this, check `themes/YOUR-THEME/templates/catalog/_partials/product-cover-thumbnails.tpl` and ensure `{$product.cover.legend}` or `{$product.cover.alt}` is assigned to the `alt` attribute.
- Clear the SmartCache: Advanced Parameters → Performance → Clear cache, then verify in DevTools.
{* Product cover image with alt text from image caption/legend *}
<img
class="js-qv-product-cover"
src="{$product.cover.bySize.large_default.url}"
alt="{$product.cover.legend|escape:'html':'UTF-8'}"
title="{$product.name|escape:'html':'UTF-8'}"
width="{$product.cover.bySize.large_default.width}"
height="{$product.cover.bySize.large_default.height}"
loading="lazy">In themes/YOUR-THEME/templates/catalog/_partials/product-cover-thumbnails.tpl — ensures the image legend (alt text) is output.
Low performance scoreLarger project
Improve your Lighthouse/PageSpeed performance score by reducing page weight, eliminating render-blocking resources, optimising images, and addressing Core Web Vitals so Google ranks your store higher in search results.
On PrestaShop
- Enable CCC (Combine, Compress, Cache): Admin → Back Office → Advanced Parameters → Performance → CCC → enable 'Smart cache for stylesheets', 'Smart cache for JavaScript', 'Minify HTML', and 'Compress inline JavaScript in HTML'.
- Enable browser caching: Advanced Parameters → Performance → Browser cache → set to enabled.
- Optimise images: Catalog → (any product) → Images tab — PrestaShop has a built-in image resizer; also install a free module like WebP Images by MyPrestaModules to serve WebP format.
- Place PrestaShop behind Cloudflare (free tier): update your domain's DNS nameservers to Cloudflare for automatic CDN, GZIP, and caching.
- Remove unused modules: Modules → Module Manager → disable and uninstall any module not actively in use — modules inject JS/CSS on every page load.
- Re-test with PageSpeed Insights.
<!-- In <head>: preconnect to critical third-party origins to reduce connection latency -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Preload your Largest Contentful Paint image (hero/banner) so it loads earlier -->
<link rel="preload" as="image" href="/images/hero.webp" fetchpriority="high">
<!-- Hero image: eager load, explicit dimensions to prevent CLS -->
<img
src="/images/hero.webp"
alt="Descriptive alt text for your hero image"
width="1200"
height="600"
fetchpriority="high"
decoding="async"
>
<!-- All other images: lazy load, explicit dimensions to prevent CLS -->
<img
src="/images/product-shot.webp"
alt="Descriptive product alt text"
width="800"
height="800"
loading="lazy"
decoding="async"
>
<!-- Defer non-critical third-party scripts (analytics, chat, pixels) -->
<script src="https://example-analytics.com/tracker.js" defer></script>
<!-- Load non-critical CSS asynchronously to unblock render -->
<link rel="preload" href="/css/non-critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/css/non-critical.css"></noscript>Paste these patterns into your <head> and image markup to address the most common Lighthouse failures.
Meta description too longQuick win
Shorten your meta description to 150–160 characters so Google displays it in full instead of cutting it off with "…" in search results.
On PrestaShop
- Go to Catalog → Products → click the product → select the 'SEO' tab.
- Edit the 'Meta description' field, keeping it under 160 characters.
- For CMS pages: go to Design → Pages → click the page → edit the 'Meta description' field.
- Save the record.
<meta name="description" content="Award-winning Bang & Olufsen headphones, speakers and TVs. Visit our Malaysia showroom at The Gardens Mall, Level 2. Free shipping on orders over RM500.">Place this inside the <head> element of every page. Replace the content value with your page-specific description (max 160 characters).
Meta description too shortModerate effort
Expand your meta description to 120–160 characters so Google displays a compelling, complete snippet in search results that drives more clicks to your store.
On PrestaShop
- Admin → Catalog → Products → select the product → 'SEO' tab → update the 'Meta description' field → Save.
- For categories: Admin → Catalog → Categories → select the category → 'SEO' tab → update 'Meta description' → Save.
- For CMS pages: Admin → Design → Pages → select the page → update 'Meta description' → Save.
<meta name="description" content="Shop the official Nike national team jerseys — advanced Dri-FIT heat tech meets bold heritage design. Free shipping on orders over $75. Explore the full collection now.">Place this inside the <head> element of your HTML page. Replace the content value with your 120–160 character description.
Missing canonicalModerate effort
Add a self-referencing canonical tag to every page so Google knows which URL is the definitive version of that content.
On PrestaShop
- Go to Shop Parameters → Traffic & SEO in your PrestaShop Admin.
- Ensure the SEO & URLs settings are configured and 'Friendly URLs' are enabled — PrestaShop's built-in canonical handling is tied to clean URL structure.
- Install a dedicated SEO module such as 'SEO Expert' or 'Yoast SEO for PrestaShop' from the PrestaShop Marketplace, which adds explicit `<link rel='canonical'>` tags to all page types.
- Once installed, configure the module to enable canonical tags for product, category, and CMS pages.
- Verify by viewing page source and searching for 'canonical'.
<link rel="canonical" href="https://www.yourstore.com/your-page/" />Paste this inside the <head> element of every page. Replace the href value with the full, absolute preferred URL of that specific page.
Missing h1Quick win
Add a single, descriptive H1 tag to every page that currently lacks one, clearly stating the page's main topic.
On PrestaShop
- For product pages: Catalog → Products → open the product → the 'Product Name' field is output as the H1 in the default product page template (catalog/product.tpl or themes/<theme>/templates/catalog/product.tpl).
- For category pages: Catalog → Categories → edit the category → the 'Name' field renders as the H1.
- For CMS pages: Design → Pages → open the page → add a heading in the content editor and style it as Heading 1, or ensure the page Title is rendered via <h1> in your CMS page template.
- If the theme template does not output an H1, edit themes/<your_theme>/templates/catalog/product.tpl and wrap {$product.name} in <h1> tags.
- Clear the cache under Advanced Parameters → Performance → Clear cache.
<h1>Women's Running Shoes</h1>Place exactly one of these per page, as the first heading in the main content area.
Missing meta descriptionModerate effort
Write and add a unique meta description (120–160 characters) to every page that is missing one, so Google can display an accurate, compelling snippet in search results.
On PrestaShop
- For a product: Back Office → Catalog → Products → click the product → 'SEO' tab → fill in 'Meta description'.
- For a category: Back Office → Catalog → Categories → click the category → 'SEO' tab → 'Meta description'.
- For a CMS page: Back Office → Design → Pages → click the page → 'SEO' tab → 'Meta description'.
- For the homepage: Back Office → Shop Parameters → Traffic & SEO → SEO & URLs → click 'index' → 'Meta description' field.
<head>
<meta charset="UTF-8">
<meta name="description" content="Shop our full range of handmade leather wallets. Free shipping on orders over $50. Slim, durable & personalised — order yours today.">
<title>Handmade Leather Wallets | YourStoreName</title>
</head>Place this tag inside the <head> element of every HTML page. Keep the content between 120–160 characters.
Missing og descriptionModerate effort
Add an og:description meta tag to every page so that social media platforms display a compelling, accurate preview when your pages are shared.
On PrestaShop
- Install the free 'SEO Expert' or 'Facebook Open Graph Tags' module from PrestaShop Addons, or use a comprehensive SEO module.
- Alternatively, edit your active theme's header.tpl file (located under themes/[your-theme]/templates/_partials/head.tpl) and add: <meta property="og:description" content="{$page.meta.description|escape:'html':'UTF-8'}">.
- Clear the Smarty cache in Advanced Parameters → Performance → Clear Cache.
- Verify with Facebook Sharing Debugger.
<head>
<!-- Open Graph / Social Sharing -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://www.example.com/page-url/" />
<meta property="og:title" content="Page Title – Brand Name" />
<meta property="og:description" content="A concise, compelling summary of this page in 100–160 characters that encourages clicks." />
<meta property="og:image" content="https://www.example.com/images/social-preview.jpg" />
</head>Place all Open Graph tags inside the <head> element of every page.
Missing og imageModerate effort
Add an og:image meta tag to every page so that social media platforms and messaging apps display a rich, branded image preview whenever someone shares your store's link.
On PrestaShop
- Go to Modules → Module Catalog and search for an SEO module such as 'SEO Expert' or 'Open Graph Tags' — install and configure it.
- In the module settings, map the og:image field to the product's cover/main image for product pages, and set a static fallback image URL for other page types.
- Alternatively, edit your theme's header.tpl file (located in themes/[your-theme]/templates/_partials/head.tpl) to manually add og:image meta tags using Smarty variables like {$product.cover.bySize.large_default.url}.
- Clear the PrestaShop cache (Advanced Parameters → Performance → Clear cache) and validate with the Meta Sharing Debugger.
Missing og titleQuick win
Add an og:title meta tag to every page so your store's title appears correctly when shared on social media platforms like Facebook, LinkedIn, and Pinterest.
On PrestaShop
- In your PrestaShop admin, go to Modules → Module Manager and install a module such as 'SEO Expert' or 'Open Graph Tags' to add og:title support site-wide.
- Alternatively, edit your theme's head.tpl file (usually at themes/[your-theme]/templates/_partials/head.tpl) and add: <meta property="og:title" content="{$meta_title|escape:'htmlall':'UTF-8'}"> inside the <head> block.
- The {$meta_title} variable is populated from each page's SEO title, which you can set per product by going to Catalog → Products → edit a product → SEO tab → 'Meta title' field.
- Clear the PrestaShop cache (Advanced Parameters → Performance → Clear cache) and verify with Facebook Sharing Debugger.
{* Inside <head> in themes/[your-theme]/templates/_partials/head.tpl *}
<meta property="og:title" content="{$meta_title|escape:'htmlall':'UTF-8'}" />
<meta property="og:type" content="website" />
<meta property="og:url" content="{$urls.current_url|escape:'htmlall':'UTF-8'}" />
<meta property="og:site_name" content="{$shop.name|escape:'htmlall':'UTF-8'}" />Add inside the <head> block in themes/[your-theme]/templates/_partials/head.tpl.
Missing titleQuick win
Add a unique, descriptive title tag (30–60 characters) to every page that is missing one.
On PrestaShop
- For a product: go to Catalog → Products → select the product → click the 'SEO' tab → fill in 'Meta title'.
- For a category: go to Catalog → Categories → select the category → 'SEO' tab → 'Meta title'.
- For a CMS page: go to Design → Pages → select the page → fill in 'Meta title'.
- Click Save.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Slim Bifold Leather Wallet | BrandName</title>
</head>
<body>
<!-- page content -->
</body>
</html>Place this inside the <head> element of every HTML page. Keep the text between 30–60 characters.
Multiple h1Moderate effort
Remove all but one H1 tag from each page so every page has exactly one clear, descriptive primary heading.
On PrestaShop
- Via FTP or your hosting file manager, navigate to themes/<your-theme>/templates/catalog/ and open product.tpl for product pages and category.tpl (or listing/product-list.tpl) for category pages.
- Search for '<h1' — only the main product or category name should be in an H1. Change any other H1 instances to H2 or H3.
- Check themes/<your-theme>/templates/_partials/product-cover-thumbnails.tpl and miniatures/product.tpl for H1 tags in product cards — change these to H2 or H3.
- Clear the theme cache in PrestaShop Admin → Advanced Parameters → Performance → Clear cache.
- Verify with browser DevTools.
<!-- CORRECT: One H1 per page -->
<h1>Blue Running Shoes – Men's Lightweight Trainers</h1>
<!-- Major sections use H2 -->
<h2>Product Details</h2>
<p>Breathable mesh upper, responsive foam midsole...</p>
<h2>Customer Reviews</h2>
<!-- Sub-sections within a major section use H3 -->
<h3>Top Review: 5 Stars</h3>
<p>Best shoes I've ever bought!</p>
<!-- WRONG: Multiple H1 tags — do NOT do this -->
<!--
<h1>Blue Running Shoes</h1>
<h1>Product Details</h1>
<h1>Customer Reviews</h1>
-->Correct semantic heading structure for an ecommerce page — one H1, everything else H2 or lower.
Noindex detectedQuick win
Remove or replace the noindex directive on any page you want to appear in Google search results.
On PrestaShop
- Admin → Shop Parameters → Traffic & SEO → SEO & URLs — edit the page meta entry and ensure the 'Indexed' option is enabled.
- For product/category pages: Catalog → Products (or Categories) → edit item → SEO tab → check the Robots field and set to `index, follow`.
- Also check your theme's `meta.tpl` or `head.tpl` files in `/themes/your-theme/templates/_partials/` for any hard-coded noindex tags.
<meta name="robots" content="index,follow">Place inside <head>. This is the CORRECT tag that allows indexing and link-following.
Non self canonicalQuick win
Audit every page whose canonical tag points to a different URL, then either correct it to self-reference or confirm the cross-reference is intentional and correct.
On PrestaShop
- PrestaShop generates canonical URLs automatically for product and category pages through its core SEO settings.
- Go to Shop Parameters → Traffic & SEO in the PrestaShop admin panel. Check the 'Set up URLs' section and ensure your preferred domain format (www vs non-www, http vs https) is correctly configured — this affects what the canonical outputs.
- For individual product pages: Catalog → Products → edit the product → go to the 'SEO' tab and check the 'Friendly URL' field. The canonical is built from this; correct it if it's wrong.
- If a third-party SEO module (e.g., SEO Expert, Advanced SEO) is installed, open the module configuration and look for per-page canonical override settings.
- Clear the PrestaShop cache (Advanced Parameters → Performance → Clear cache) and view-source the frontend page to confirm the canonical is correct.
<link rel="canonical" href="https://www.example.com/your-page-url/">Place this tag inside the <head> element of every page. The href must exactly match the page's own preferred URL.
Page not foundModerate effort
Fix broken internal links pointing to pages that return a 404 error by either removing those links or setting up a 301 redirect from the missing URL to the correct replacement page.
On PrestaShop
- In the PrestaShop back office, go to Shop Parameters → Traffic & SEO → URL Redirections (or install the free '301 Redirections' module from the PrestaShop Marketplace).
- Add a new redirect: enter the old URL path and the new destination URL, select '301 Moved Permanently', and save.
- To fix internal links in CMS pages, go to Design → Pages, open the affected page, and edit the link in the TinyMCE editor.
- For links in the theme template files (.tpl files), edit them via FTP or your hosting file manager under /themes/[your-theme]/.
- Clear the PrestaShop cache (Advanced Parameters → Performance → Clear Cache) after making changes.
- Verify the old URL now redirects with a 301 status using a browser or HTTP status checker.
<!-- BEFORE: broken internal link -->
<a href="/old-products/discontinued-item">Shop Now</a>
<!-- AFTER: updated to correct URL -->
<a href="/products/replacement-item">Shop Now</a>Update an internal anchor link to point to the correct URL instead of the broken one.
Page speed warningModerate effort
Reduce your page load time to under 1.5 seconds by optimizing images, eliminating render-blocking resources, and enabling caching and compression.
On PrestaShop
- In the PrestaShop admin go to Advanced Parameters > Performance: set 'Smart cache for CSS' and 'Smart cache for JavaScript' to Yes, enable CCC (Combine, Compress, Cache) for both CSS and JS, and set 'Minimize HTML' to Yes.
- Enable 'Use CCC for CSS files', 'Use CCC for JavaScript files', and 'Compress inline JavaScript' all from the same Performance page.
- Images: Go to Design > Image Settings and configure WebP format output. Then go to Catalog > Products and use the 'Regenerate Thumbnails' option after changing settings to rebuild all product images at optimal sizes.
- Caching: On the Performance page, under 'Caching', enable the file system cache. For high-traffic stores add a Memcached or Redis module from the PrestaShop Addons marketplace.
- Modules: Go to Modules > Module Manager and disable or uninstall modules you are not using. Filter by 'Installed' to audit the full list.
Search results indexableModerate effort
Add a noindex robots meta tag to all internal search result pages and block the search URL path in robots.txt to prevent Google from crawling and indexing thin, duplicate search results.
On PrestaShop
- In your PrestaShop back office, go to Shop Parameters → Traffic & SEO → SEO & URLs.
- Find the 'search' page entry and click Edit — add noindex,follow to the robots meta field if your PrestaShop version supports it, or install a dedicated SEO module (e.g., 'SEO Expert' or 'Yoast SEO for PrestaShop') that provides per-template robots tag control.
- In your theme, open themes/<your-theme>/templates/catalog/search.tpl and add <meta name="robots" content="noindex,follow"> inside the <head> block (or conditionally in the parent _partials/head.tpl).
- For robots.txt: go to Shop Parameters → Traffic & SEO → scroll to the robots.txt section → add Disallow: /search and Disallow: /?s= as relevant, then regenerate the file.
- Verify with Google Search Console URL Inspection.
<meta name="robots" content="noindex,follow">Place this tag inside the <head> element of every search results page template.
Seo category description missingModerate effort
Write a 100–200 word description for every category page explaining what products it contains and who they are for, so Google can rank it for category-level keywords.
On PrestaShop
- In the PrestaShop back office, go to Catalog → Categories.
- Click Edit next to the category you want to update.
- Scroll to the 'Description' field — it has a built-in rich-text (TinyMCE) editor. Write or paste your 100–200 word description.
- Click Save.
- The description is displayed in your theme's category.tpl template file (themes/[your-theme]/templates/catalog/category.tpl). Most default themes (Classic, Hummingbird) display it above the product list automatically.
<section class="category-description">
<h1>Women's Waterproof Hiking Boots</h1>
<p>
Our women's waterproof hiking boots are built for trail runners, weekend
trekkers, and serious mountaineers who refuse to let wet weather slow them
down. Every pair in this collection features a fully sealed waterproof
membrane, aggressive rubber outsoles for grip on loose or muddy terrain,
and reinforced ankle support to protect against sprains on uneven ground.
Whether you're day-hiking a local forest trail or taking on a multi-day
alpine route, you'll find boots here rated for every conditions and
experience level. Lightweight options keep pack weight low on fast-and-light
adventures, while insulated models keep feet warm below freezing. Browse the
full range below to find your perfect fit.
</p>
</section>Place this block near the top of your category page, above the product grid.
Seo category description thinModerate effort
Expand your category page description to at least 100 words of genuinely helpful, keyword-rich content that explains what shoppers will find in that category.
On PrestaShop
- In your PrestaShop back office, go to Catalog > Categories.
- Click 'Edit' on the category you want to update.
- Find the 'Description' rich-text field and write your expanded 100+ word description.
- PrestaShop also has a 'Meta description' field lower on the same page — fill that in separately (150–160 characters).
- Click 'Save'. The description appears on the category page wherever your theme calls {$category.description}.
<section class="category-description">
<h2>Shop Our Women's Running Shoes</h2>
<p>
Discover our curated collection of women's running shoes, designed for every
type of runner — from casual joggers to marathon enthusiasts. Whether you
need extra cushioning for long-distance road running, a lightweight racer
for speed training, or a grippy trail shoe for off-road adventures, you'll
find the perfect fit here.
</p>
<p>
We stock top brands including Brooks, ASICS, New Balance, and Hoka, all
rigorously tested for comfort, durability, and performance. Each pair is
available in a wide range of widths and sizes, so you never have to
compromise on fit. Browse by surface type, cushion level, or pronation
support to narrow down your ideal shoe in seconds.
</p>
<p>
Not sure where to start? Check out our
<a href="/guides/running-shoe-guide">Running Shoe Buying Guide</a> or
explore our <a href="/collections/running-accessories">Running Accessories</a>
collection to complete your kit.
</p>
</section>Paste this pattern into any category page template. Replace placeholder text with your real content.
Seo category emptyQuick win
Either populate the empty category with products, or add a noindex tag and remove it from your navigation so Google does not waste crawl budget on a page with no content.
On PrestaShop
- In your PrestaShop back office, go to Catalog → Categories.
- Click 'Edit' on the empty category.
- Option A — Add products: Go to Catalog → Products, edit each product, and in the 'Categories' tab assign it to this category.
- Option B — Noindex: PrestaShop does not have a built-in per-category noindex toggle. Install the 'SEO Expert' or 'Prestashop SEO' module. Alternatively, edit the category.tpl file in your theme (themes/[your-theme]/templates/catalog/category.tpl) and add a conditional: {if $category.nb_products == 0}<meta name='robots' content='noindex, follow'>{/if} inside the {block name='head_seo'} or equivalent block.
- Option C — Delete & redirect: Delete the category in Catalog → Categories. Go to Shop Parameters → Traffic & SEO → URL Redirections and add a 301 redirect from the old category URL to the parent category URL.
- Disable the category display in menus by toggling 'Displayed' to off in the category editor, or by removing it from the menu via Modules → Main Menu.
{if isset($category.nb_products) && $category.nb_products == 0}
<meta name="robots" content="noindex, follow">
{/if}Add inside the {block name='head'} or equivalent section in themes/[your-theme]/templates/catalog/category.tpl.
Seo category sparseModerate effort
Add more products to sparse category pages, or consolidate very thin categories into broader, related ones so every indexed category page delivers genuine value to shoppers and search engines.
On PrestaShop
- Go to Catalog → Categories in your PrestaShop back office.
- Click 'Edit' on the sparse category. Add a Description (HTML supported) for indexable on-page content.
- To add products: open each product → go to the 'Categories' tab → tick the target category. Or use the product list in the category editor.
- To merge: move all products to the surviving category, delete the old one, and add a 301 redirect via a module (e.g., 'Friendly URL Redirect' or edit the .htaccess for Apache servers).
- To noindex: in the category 'SEO' tab, clear the 'Meta title' and set a robots meta using a module such as 'SEO Expert' that provides per-category noindex options.
<meta name="robots" content="noindex, follow">Add inside <head> of any sparse category page you want excluded from Google's index while still being followable by crawlers.
Seo facet url not canonicalizedModerate effort
Add a canonical tag to every filtered, sorted, and paginated collection/category URL pointing back to the clean (unfiltered) category URL, so Google consolidates duplicate pages and focuses crawl budget on your real content.
On PrestaShop
- PrestaShop generates canonical tags via its core SEO module. Go to Shop Parameters → SEO & URLs and enable 'Canonical URL' if available in your version.
- For faceted search (the native Faceted Search module or ps_facetedsearch): go to Modules → Module Manager → Faceted Search → Configure. Look for a 'Generate canonical URL' or 'SEO' option and enable it — this sets the canonical of filtered pages to the clean category URL.
- If the module does not provide this option, edit the ps_facetedsearch module template or override the canonical tag in your theme's header.tpl to output the base category URL for all layered navigation pages.
- Clear cache: Advanced Parameters → Performance → Clear cache. Verify with Google Search Console.
<head>
<!-- All other head tags -->
<link rel="canonical" href="https://www.example.com/collections/hoops">
</head>Place this inside the <head> of every filtered/sorted/paginated category page. The href must be the full, absolute clean URL (no query parameters).
Seo product description minimalModerate effort
Expand your product descriptions to at least 300 words by adding specifications, use cases, and benefits so search engines have enough content to rank and shoppers have enough information to buy.
On PrestaShop
- Go to PrestaShop Admin → Catalog → Products.
- Click the product and select the 'Basic Settings' tab.
- Find the 'Description' field (long description, below the 'Summary' short description).
- Write a 300+ word structured description using the TinyMCE editor — add headings, bulleted spec lists, and benefit paragraphs.
- Click 'Save'. Clear the PrestaShop cache under Advanced Parameters → Performance if caching is enabled.
<section class="product-description">
<h2>About the [Product Name]</h2>
<p>
The [Product Name] is a [category] designed for [target audience]. Whether you need
[use case 1] or [use case 2], this [product] delivers [core benefit] without
[common pain point].
</p>
<h3>Key Features & Specifications</h3>
<ul>
<li><strong>Material:</strong> [e.g. 100% organic cotton, BPA-free polypropylene]</li>
<li><strong>Dimensions:</strong> [W × H × D] / Weight: [X kg / X lbs]</li>
<li><strong>Colour options:</strong> [Colour 1], [Colour 2], [Colour 3]</li>
<li><strong>Compatibility:</strong> [e.g. Works with iOS 14+ and Android 10+]</li>
<li><strong>Warranty:</strong> [X-year manufacturer warranty]</li>
</ul>
<h3>Why Shoppers Choose [Product Name]</h3>
<p>
Unlike [generic alternatives], the [Product Name] [specific differentiator]. This means
[tangible benefit for the shopper], saving you [time / money / effort].
</p>
<p>
Ideal for [persona / scenario], the [Product Name] has been tested to [standard or
certification] and ships ready to use — no assembly required.
</p>
<h3>How to Use</h3>
<p>
[Step-by-step or scenario-based usage instructions in 2–4 sentences. Include any
important setup, safety, or care notes here.]
</p>
<h3>In the Box</h3>
<ul>
<li>1 × [Product Name]</li>
<li>1 × [Accessory or manual]</li>
<li>1 × [Charging cable / carry bag / etc.]</li>
</ul>
</section>Structural template for a product description section — paste inside your product page's main content area.
Seo product description missingModerate effort
Write and publish a 200–400 word product description covering what the product is, its key specs, who it's for, and how to use it.
On PrestaShop
- In your PrestaShop back office, go to Catalog → Products.
- Click 'Edit' on the product.
- In the 'Basic Settings' tab, scroll to the 'Description' rich-text editor (the large TinyMCE box) and enter your full 200–400 word description. The shorter field above it is the 'Short Description' used in listing pages.
- Go to the 'SEO' tab to set a custom Meta Description.
- Click 'Save' and view the live product URL.
<section class="product-description">
<h2>About This Product</h2>
<p>
The <strong>Trailblazer 500 Waterproof Hiking Boot</strong> is a lightweight,
full-grain leather boot built for all-day comfort on rugged mountain trails.
Its Gore-Tex membrane keeps feet dry in rain and creek crossings while the
Vibram outsole delivers confident grip on wet rock and loose scree.
</p>
<p>
Designed for day hikers and weekend backpackers who want reliable protection
without the weight of a mountaineering boot, the Trailblazer 500 strikes the
ideal balance between support and agility. The padded collar and gusseted
tongue prevent trail debris from entering the boot, and the cushioned EVA
midsole absorbs impact mile after mile.
</p>
<h3>Key Specifications</h3>
<ul>
<li><strong>Upper:</strong> Full-grain leather with Gore-Tex waterproof lining</li>
<li><strong>Outsole:</strong> Vibram Megagrip rubber</li>
<li><strong>Weight:</strong> 680 g / pair (size UK 9)</li>
<li><strong>Shaft height:</strong> Mid (ankle) cut</li>
<li><strong>Available widths:</strong> Standard and Wide</li>
</ul>
<h3>How to Use</h3>
<p>
Lace firmly for ankle support on uneven terrain. Break the boot in over two
or three shorter walks before a full-day hike. Apply a water-repellent spray
to the leather upper every 20–30 hours of trail use to maintain waterproofing.
</p>
</section>Minimum viable product description markup — paste inside your product page's <main> or article element.
Seo product description thinModerate effort
Expand every product description to at least 300 words of unique, keyword-rich content that answers real shopper questions and gives Google enough text to understand and rank the page.
On PrestaShop
- Go to Back Office → Catalog → Products → [select the product].
- Under the 'Basic Settings' tab, find both 'Summary' (short description, shown in listings) and 'Description' (full, shown on product page).
- Click into 'Description' and use the TinyMCE rich-text editor to write your expanded copy.
- Save and clear the shop's cache: Advanced Parameters → Performance → Clear cache.
<section class="product-description" aria-label="Product description">
<h2>About This Product</h2>
<p>
Write your opening paragraph here (2–3 sentences introducing the product,
its primary benefit, and who it is for). Aim for a natural, engaging tone
that mirrors how your customers talk about this type of product.
</p>
<h3>Key Features</h3>
<ul>
<li><strong>Feature One:</strong> Explain the benefit, not just the spec.</li>
<li><strong>Feature Two:</strong> Include the detail a shopper needs to decide.</li>
<li><strong>Feature Three:</strong> Use the words your customers actually search for.</li>
<li><strong>Feature Four:</strong> Keep each bullet to one clear point.</li>
</ul>
<h3>Materials & Specifications</h3>
<p>
Describe materials, dimensions, weight, compatibility, or other technical
details relevant to your product category. Specificity builds trust and
captures long-tail searches (e.g. "100% organic cotton", "fits 13-inch laptops").
</p>
<h3>How to Use</h3>
<p>
Walk the customer through setup, application, or use in plain language.
Answer the most common pre-purchase questions here so shoppers feel confident.
</p>
<h3>Why Our Customers Love It</h3>
<p>
Reinforce with a sentence or two of social proof context or a unique selling
point that differentiates this product from generic alternatives.
</p>
</section>Ideal semantic structure for a product description section on any platform's product page template.
Seo product image alt filenameModerate effort
Replace auto-generated filename-based alt text on product images with short, descriptive phrases that accurately describe what the image shows.
On PrestaShop
- Go to Back Office → Catalog → Products and open a product.
- Click the 'Images' tab.
- Click an image to select it; a caption/alt text field appears.
- Enter your descriptive alt text in the 'Caption' field (PrestaShop uses this as the image alt attribute).
- Click the checkmark or Save. Repeat for each image and each product.
<!-- BAD: filename-based alt text -->
<img src="/images/IMG_4823_blue_v2.webp" alt="IMG_4823_blue_v2.webp">
<!-- BAD: empty alt text -->
<img src="/images/blue-sweater.webp" alt="">
<!-- GOOD: descriptive alt text -->
<img src="/images/blue-sweater.webp" alt="Blue cotton crew-neck sweater, front view">The correct pattern for a product image alt attribute — descriptive, specific, keyword-natural.
Seo product missing brandModerate effort
Add a `brand` property to your Product schema markup so search engines can match your listings to brand-specific queries.
On PrestaShop
- Install an SEO module that manages structured data, such as 'SEO Expert' or 'Structured Data Manager' from the PrestaShop Addons marketplace.
- Alternatively, edit your active theme's product template: `themes/<your-theme>/templates/catalog/product.tpl` — find or add the JSON-LD block and insert the brand property using the Smarty variable `{$product.manufacturer_name}`: `"brand": {"@type": "Brand", "name": "{$product.manufacturer_name|escape:'html':'UTF-8'}"}` .
- Ensure all products have a Manufacturer assigned: go to Catalog → Manufacturers and link each product.
- Clear PrestaShop cache (Advanced Parameters → Performance → Clear cache) and test with Google's Rich Results Test.
Seo product missing gtinModerate effort
Add a GTIN (and/or MPN) field to your Product structured data so Google can match your listings to its product knowledge graph for rich results and Google Shopping.
On PrestaShop
- Go to Catalog → Products → [select a product] → 'Shipping' or 'Details' tab.
- Enter the GTIN (EAN-13 or UPC) in the native 'EAN-13 or JAN barcode' and 'UPC barcode' fields, and MPN in the 'MPN' field — these are built-in PrestaShop fields.
- Install a structured data module (e.g. 'SEO Expert' or 'Structured Data Markup') from the PrestaShop Addons marketplace to read these native fields and output them as 'gtin' and 'mpn' in the Product JSON-LD.
- After configuring the module, validate a product page with Google's Rich Results Test.
Seo product missing stock statusModerate effort
Add a visible stock availability label and an `availability` property to your Product structured data so Google can display rich results and shoppers know immediately whether a product is in stock.
On PrestaShop
- PrestaShop 1.7+ includes basic Product structured data, but `availability` is often absent or incomplete.
- Go to Modules → search for *SEO Expert* or install *JSON-LD – Structured Data* from PrestaShop Addons Marketplace — these modules inject a full Product schema including `availability` mapped to your real stock.
- Alternatively, edit your theme's `product.tpl` file (found in `/themes/<your-theme>/templates/catalog/product.tpl`) and add a JSON-LD script block using Smarty conditionals: `{if $product.availability == 'available'}https://schema.org/InStock{else}https://schema.org/OutOfStock{/if}`.
- Ensure each product has 'Manage stock' enabled in Catalog → Products → Edit → Quantities tab.
- Validate in the Rich Results Test after clearing PrestaShop's cache (Advanced Parameters → Performance → Clear cache).
<!-- Product structured data with availability -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Your Product Name",
"description": "A short description of your product.",
"sku": "YOUR-SKU-123",
"image": "https://www.example.com/images/product.jpg",
"brand": {
"@type": "Brand",
"name": "Your Brand Name"
},
"offers": {
"@type": "Offer",
"url": "https://www.example.com/products/your-product",
"priceCurrency": "USD",
"price": "29.99",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition"
}
}
</script>
<!-- Visible stock label near Add-to-Cart button -->
<p class="stock-status" style="font-weight:bold; color:#2a7a2a;">✓ In Stock</p>Paste the complete block into the <head> section of your product page HTML. Change availability value to OutOfStock or PreOrder as needed.
Seo product no imagesModerate effort
Add at least one high-quality image to every product page so shoppers can see what they're buying and Google can surface your products in Image Search.
On PrestaShop
- Log in to PrestaShop Back Office and go to Catalog → Products.
- Click 'Edit' on the product missing images.
- Click the 'Images' tab in the left sidebar of the product edit page.
- Drag and drop image files into the upload area, or click to browse and select files.
- After uploading, click each image thumbnail and fill in the 'Caption' field (used as alt text) for each language if your store is multilingual.
- Click 'Save and stay' and then visit the front-office product page to verify images are visible.
<figure class="product-gallery">
<img
src="/images/products/black-leather-wallet-front.jpg"
alt="Black leather wallet with RFID blocking, front view"
width="800"
height="800"
loading="eager"
/>
<img
src="/images/products/black-leather-wallet-open.jpg"
alt="Black leather wallet open showing card slots and cash pocket"
width="800"
height="800"
loading="lazy"
/>
</figure>Basic responsive product image with descriptive alt text — place inside your product page HTML where the gallery/main image appears.
Seo product no spec tableModerate effort
Add a structured specifications table or list to every product page so shoppers, search engines, and AI tools can instantly find and use key product details.
On PrestaShop
- In the PrestaShop back office go to Catalog → Products → (select product) → 'Features' tab.
- Click 'Add a feature', select an existing feature (or create one via Catalog → Attributes & Features → Features), and enter the value for this product.
- PrestaShop automatically renders assigned features as a 'Data Sheet' or 'Features' section on the product page in most themes.
- To customise the layout, edit the theme template file at 'themes/<your-theme>/templates/catalog/_partials/product-details.tpl' and style the feature list as a table.
- Save and preview the product page.
Seo product price not in htmlModerate effort
Render your product price in the server-generated HTML so Google and other crawlers can read it directly — without executing JavaScript.
On PrestaShop
- PrestaShop renders prices via Smarty templates. Open themes/<your-theme>/templates/catalog/product.tpl and locate the price display block.
- Ensure the price is output using the native Smarty variable {$product.price} (or {$product.regular_price}) directly in the template — not loaded via an AJAX call.
- For JSON-LD structured data, PrestaShop 1.7+ includes a structured data hook. Go to Modules → Module Manager → search for 'ps_structureddata' — enable it. This module outputs Product schema including price in server HTML.
- View page source after clearing cache (Advanced Parameters → Performance → Clear Cache) and confirm price is present.
Seo product single imageModerate effort
Add at least 4 product images (multiple angles, detail shots, and lifestyle/in-use photos) to every product page to improve click-through rates, conversions, and Google Images visibility.
On PrestaShop
- Go to Back Office → Catalog → Products → [select your product] → Images tab.
- Click 'Choose files' to upload additional product images in bulk.
- For each uploaded image, click its thumbnail, then fill in the 'Caption' field (used as alt text) for each language.
- Drag images by their handles to set the desired display order — the first image is the cover/main image.
- Click 'Save' to publish changes.
<figure class="product-gallery">
<!-- Main hero image -->
<img
src="/images/red-leather-wallet-front.webp"
alt="Red leather bifold wallet — front view"
width="2000"
height="2000"
loading="eager"
fetchpriority="high"
/>
</figure>
<!-- Thumbnail strip -->
<ul class="product-thumbnails" role="list">
<li>
<img
src="/images/red-leather-wallet-front-thumb.webp"
alt="Red leather bifold wallet — front view"
width="200"
height="200"
loading="lazy"
/>
</li>
<li>
<img
src="/images/red-leather-wallet-back-thumb.webp"
alt="Red leather bifold wallet — back view"
width="200"
height="200"
loading="lazy"
/>
</li>
<li>
<img
src="/images/red-leather-wallet-interior-thumb.webp"
alt="Red leather bifold wallet — interior card slots"
width="200"
height="200"
loading="lazy"
/>
</li>
<li>
<img
src="/images/red-leather-wallet-lifestyle-thumb.webp"
alt="Red leather bifold wallet shown in a shirt pocket"
width="200"
height="200"
loading="lazy"
/>
</li>
</ul>Basic accessible product image gallery markup. Place inside your product detail page template.
Seo product title genericModerate effort
Rewrite every product page title tag to include the specific product name plus at least one differentiating attribute (variant, material, brand, or model) so Google and shoppers can instantly tell what the page is about.
On PrestaShop
- Go to Catalog → Products in your PrestaShop back office and click the product.
- Select the 'SEO' tab within the product editor.
- Update the 'Meta title' field with your descriptive product title.
- Click Save.
- For bulk updates, use a module such as 'Easy SEO' or export products via the native CSV export, edit the 'meta_title' column, and re-import.
<head>
<title>Magnetic Card Holder Phone Mount – MagSafe Compatible – BrandName</title>
<meta name="description" content="Attach your cards securely to any MagSafe-compatible iPhone with BrandName's slim magnetic card holder mount. Holds 1–3 cards. Available in black and white.">
</head>Place inside the <head> section of every product page. Follow the pattern: Product Name – Key Attribute – Brand.
Seo variant urls in sitemapModerate effort
Remove product variant URLs (e.g. ?variant= or ?sku= query strings) from your XML sitemap and keep only the canonical base product URL for each product.
On PrestaShop
- Go to Admin → Modules → Module Manager → search for 'Google Sitemap' (the native module).
- Open the module configuration and look for 'Exclude dynamic URLs' or 'URL parameters to exclude' — add 'variant', 'sku', 'color', 'size', or whichever parameters your store uses.
- If using a third-party SEO module (e.g. SEO Expert, Yoast for PrestaShop), find its Sitemap settings and disable 'include product combinations/attributes in sitemap'.
- Regenerate the sitemap from the module settings page and verify the output XML.
- Re-submit in Google Search Console.
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<!-- CORRECT: canonical base product URL -->
<url>
<loc>https://www.example.com/products/blue-sneaker</loc>
<lastmod>2024-06-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<!-- WRONG — never include variant query strings:
<url>
<loc>https://www.example.com/products/blue-sneaker?variant=12345</loc>
</url>
<url>
<loc>https://www.example.com/products/blue-sneaker?sku=blue-large</loc>
</url>
-->
</urlset>Correct sitemap entry — clean base product URL only, no query parameters.
Server errorModerate effort
Identify and fix every page returning an HTTP 5xx server error so search engines can crawl and index those URLs again.
On PrestaShop
- Enable error display for debugging: in config/defines.inc.php (or via Admin > Advanced Parameters > Performance) set _PS_MODE_DEV_ to true temporarily.
- Check var/logs/ (PrestaShop 1.7+) or /log/ for exception logs with the exact error trace.
- Disable recently installed modules via Admin > Modules > Module Manager — deactivate and test.
- If the error is on a theme template, revert to the default Classic theme via Admin > Design > Themes.
- After fixing, set dev mode back to false and submit the URL in Google Search Console > URL Inspection > Request Indexing.
# Run this in your terminal to check the HTTP status code of any URL
curl -o /dev/null -s -w "HTTP Status: %{http_code}\n" https://www.example.com/your-page
# A healthy page returns:
# HTTP Status: 200
# A broken page returns (example):
# HTTP Status: 500This is the correct HTTP 200 response header your server should return for a healthy page. Use curl on the command line to verify any URL's status code before and after your fix.
Title too longQuick win
Shorten your page title tag to 60 characters or fewer so it displays fully — without truncation — in Google search results.
On PrestaShop
- For a product: go to Catalog → Products, open the product, click the 'SEO' tab, and edit the 'Meta title' field (≤60 characters).
- For a category: go to Catalog → Categories, open the category, select the 'SEO' tab, and edit 'Meta title'.
- For CMS pages: go to Design → Pages, open the page, and edit 'Meta title' in the SEO section.
- Save changes.
<head>
<meta charset="UTF-8">
<title>Running Shoes for Men | YourBrand</title>
</head>Place inside the <head> element of every HTML page. Keep the total character count at or under 60.
Title too shortQuick win
Expand your page title to between 30 and 60 characters so Google displays it fully in search results and shoppers can immediately understand what the page is about.
On PrestaShop
- For a product: go to Catalog → Products → select the product → click the 'SEO' tab → update the 'Meta title' field.
- For a category: go to Catalog → Categories → select the category → click the 'SEO' tab → update 'Meta title'.
- For CMS pages: go to Design → Pages → select the page → update 'Meta title'.
- For the homepage: go to Shop Parameters → Traffic & SEO → find the 'index' page → update 'Meta title'.
- Save changes; PrestaShop applies the meta title as the HTML title tag immediately.
<head>
<meta charset="UTF-8">
<title>Men's Merino Wool Beanies – BrandName</title>
</head>Place inside the <head> element of every page. Replace the example with your actual descriptive title (30–60 chars).
Answer Engine Optimization · 7 fixes
Low review countModerate effort
Collect at least 5 published reviews so Google will display star ratings in search results alongside your product listings.
On PrestaShop
- Go to Modules → Module Manager and search for 'Product Comments' — enable the native product comments module.
- Configure a post-purchase review reminder under the module's settings, or install 'Trustpilot Reviews' or 'Avis Vérifiés' from the PrestaShop Addons marketplace.
- The Product Comments module outputs basic schema; for full AggregateRating JSON-LD, install the 'SEO Expert' or 'SEO Pack' module from the marketplace.
- Once 5+ reviews are live, test with Google's Rich Results Test.
<!-- Product structured data with AggregateRating -->
<!-- Note: Google will only show review stars when reviewCount >= 5 -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Your Product Name",
"image": [
"https://example.com/images/product-front.jpg",
"https://example.com/images/product-side.jpg"
],
"description": "A clear, accurate description of your product.",
"sku": "SKU-12345",
"mpn": "MPN-12345",
"brand": {
"@type": "Brand",
"name": "Your Brand"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.5",
"reviewCount": "12",
"bestRating": "5",
"worstRating": "1"
},
"offers": {
"@type": "Offer",
"url": "https://example.com/products/your-product",
"priceCurrency": "USD",
"price": "49.99",
"priceValidUntil": "2026-12-31",
"itemCondition": "https://schema.org/NewCondition",
"availability": "https://schema.org/InStock",
"seller": {
"@type": "Organization",
"name": "Your Store Name"
}
}
}
</script>Drop this complete block into the <head> section of any product page. Swap in your real values for name, image, SKU, price, ratingValue, and reviewCount.
Missing schema breadcrumblistModerate effort
Add BreadcrumbList structured data (JSON-LD) to every page so Google can display your site's navigation path directly in search results.
On PrestaShop
- Go to your PrestaShop back office › Modules › Module Manager and search for an SEO / structured data module (e.g. 'SEO Expert' or 'Structured Data Markup').
- If adding manually, open your theme's templates directory (themes/<your-theme>/templates/) and edit the relevant .tpl files (product.tpl, category.tpl).
- Add the JSON-LD script block before </head>, using Smarty variables ({$product.name}, {$urls.pages.index}, {$product.url}) to make the values dynamic.
- Validate with Google's Rich Results Test after saving.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://www.example.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Women's Clothing",
"item": "https://www.example.com/womens-clothing/"
},
{
"@type": "ListItem",
"position": 3,
"name": "Dresses",
"item": "https://www.example.com/womens-clothing/dresses/"
},
{
"@type": "ListItem",
"position": 4,
"name": "Blue Wrap Dress",
"item": "https://www.example.com/womens-clothing/dresses/blue-wrap-dress/"
}
]
}
</script>Place this <script> block inside <head> or anywhere in <body> on any page with a breadcrumb trail. Extend itemListElement with one ListItem per level.
Missing schema faqpageQuick win
Add FAQPage schema markup to any page that contains a Frequently Asked Questions section so Google can display your answers as rich results directly in search.
On PrestaShop
- Access your back office and go to Modules › Module Manager.
- Search for and install an SEO module such as 'SEO Expert' or 'Structured Data' by PrestaHero/others from the Addons Marketplace — most include FAQPage and BreadcrumbList schema generators.
- Alternatively, edit your theme's CMS page template directly: open themes/<your-theme>/templates/cms/page.tpl and paste your <script type='application/ld+json'> FAQPage block near the closing </body>.
- For BreadcrumbList: most PrestaShop themes include a breadcrumb snippet — find templates/layouts/layout-full-width.tpl and add the BreadcrumbList JSON-LD block, substituting Smarty variables like {$breadcrumb.links} for dynamic data.
- Clear the cache under Advanced Parameters › Performance › Clear Cache after making changes.
- Validate with Google's Rich Results Test.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Frequently Asked Questions | Your Store</title>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What is your return policy?",
"acceptedAnswer": {
"@type": "Answer",
"text": "We accept returns within 30 days of purchase. Items must be unused and in their original packaging."
}
},
{
"@type": "Question",
"name": "How long does shipping take?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Standard shipping takes 5–7 business days. Expedited options are available at checkout."
}
}
]
}
</script>
</head>
<body>
<h1>Frequently Asked Questions</h1>
<h2>What is your return policy?</h2>
<p>We accept returns within 30 days of purchase. Items must be unused and in their original packaging.</p>
<h2>How long does shipping take?</h2>
<p>Standard shipping takes 5–7 business days. Expedited options are available at checkout.</p>
</body>
</html>Full HTML page example showing exactly where to place the FAQPage script block inside the <head>.
Missing schema howtoModerate effort
Add HowTo structured data (JSON-LD) to any page that walks customers through a multi-step process, so Google can display your instructions as rich results in search.
On PrestaShop
- Use a structured data module from the PrestaShop Marketplace (search 'JSON-LD schema') or add custom code.
- For manual injection: override the relevant CMS page template (cms.tpl or cms-category.tpl in your theme's /templates/cms/ folder) and add the <script type='application/ld+json'> block before </body>.
- Use a hook (hookDisplayHeader or hookDisplayFooter) in a custom module to inject JSON-LD only on designated CMS pages, checking the controller type with $this->context->controller->php_self == 'cms'.
- Validate with Google's Rich Results Test.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>How to Complete Your Task | Your Store</title>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "HowTo",
"name": "How to Complete Your Task",
"description": "Follow these steps to complete your task easily.",
"totalTime": "PT10M",
"step": [
{
"@type": "HowToStep",
"position": 1,
"name": "Prepare your materials",
"text": "Gather everything you need before you start: list materials here."
},
{
"@type": "HowToStep",
"position": 2,
"name": "Complete the main action",
"text": "Describe exactly what the user should do in this core step."
},
{
"@type": "HowToStep",
"position": 3,
"name": "Verify the result",
"text": "Explain how the user confirms the task was completed successfully."
}
]
}
</script>
</head>
<body>
<!-- Your how-to page content -->
</body>
</html>Place this block inside the <head> element of any static HTML page that contains step-by-step instructions.
Missing schema localbusinessQuick win
Add LocalBusiness JSON-LD structured data to your store so search engines and AI answer engines can surface your business name, address, phone number, and hours in rich results and direct answers.
On PrestaShop
- Go to Modules → Module Manager and install a structured data module such as 'SEO & Rich Snippets' or 'Structured Data Markup'.
- Fill in your LocalBusiness details in the module's configuration panel.
- Alternatively, edit your active theme's header.tpl file (located in themes/<your-theme>/templates/_partials/head.tpl) to paste the JSON-LD block before </head>, then clear the cache via Advanced Parameters → Performance → Clear Cache.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Your Store</title>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Store",
"name": "Your Business Name",
"url": "https://www.yourdomain.com",
"logo": "https://www.yourdomain.com/logo.png",
"address": {
"@type": "PostalAddress",
"streetAddress": "123 Main Street",
"addressLocality": "Springfield",
"addressRegion": "IL",
"postalCode": "62701",
"addressCountry": "US"
},
"telephone": "+1-217-555-0100",
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"],
"opens": "09:00",
"closes": "18:00"
}
]
}
</script>
</head>
<body>
<!-- page content -->
</body>
</html>Paste this block anywhere inside the <head> element of your HTML page. Replace all placeholder values before deploying.
Missing schema organizationQuick win
Add an Organization schema markup block to your store's homepage (and/or sitewide template) so search engines and AI assistants can definitively identify your brand.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → select your theme.
- Edit the template file themes/<yourtheme>/templates/_partials/head.tpl and paste your <script type='application/ld+json'>…</script> Organization block before the closing </head>.
- Alternatively, use a free module such as 'SEO Expert' from the PrestaShop Addons marketplace which includes Organization schema settings.
- Clear the cache via Advanced Parameters → Performance → Clear cache.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Acme Store",
"url": "https://www.acmestore.com",
"logo": {
"@type": "ImageObject",
"url": "https://www.acmestore.com/images/logo.png",
"width": 250,
"height": 250
},
"sameAs": [
"https://www.facebook.com/acmestore",
"https://www.instagram.com/acmestore",
"https://twitter.com/acmestore",
"https://www.linkedin.com/company/acmestore",
"https://www.youtube.com/c/acmestore"
],
"contactPoint": {
"@type": "ContactPoint",
"contactType": "customer service",
"email": "hello@acmestore.com"
}
}
</script>Drop this complete block inside your <head>…</head> section on every page. Replace all placeholder values.
Missing schema productModerate effort
Add Product schema markup (JSON-LD) to every product page so search engines can display rich results — including price, availability, and ratings — directly in search.
On PrestaShop
- In your PrestaShop back office, go to Modules → Module Manager and install a schema module such as 'SEO Expert' or 'Structured Data / Schema.org Markup'.
- Alternatively, edit your active theme's product template: themes/<your-theme>/templates/catalog/product.tpl and add the JSON-LD block using Smarty variables: {$product.name}, {$product.price}, {$product.reference}, {$product.cover.large.url}.
- Clear the PrestaShop cache via Advanced Parameters → Performance → Clear Cache, then validate a product page with Google's Rich Results Test.
Accessibility (WCAG) · 37 fixes
Aria allowed roleQuick win
Remove or replace any ARIA `role` attribute that is not permitted on the HTML element it is applied to — in particular, do not add `role="textbox"` (or other interactive roles) to elements that already have a native semantic role which conflicts with it.
On PrestaShop
- Navigate to your PrestaShop admin → Design → Theme & Logo → click 'Use this theme' on your active theme to confirm it, then access theme files via FTP at `/themes/<your-theme>/`.
- Search `.tpl` Smarty template files for the offending role attribute (e.g., `grep -r 'role="presentation"' themes/`).
- Edit the relevant `.tpl` file to remove or correct the role attribute.
- Clear the PrestaShop cache: Admin → Advanced Parameters → Performance → Clear cache.
- Verify with axe DevTools.
<!-- ❌ WRONG: role="textbox" is not permitted on <select> -->
<select role="textbox" name="country">
<option value="us">United States</option>
</select>
<!-- ✅ FIX A: Remove the invalid role — <select> already has the correct native semantics -->
<select name="country">
<option value="us">United States</option>
</select>
<!-- ❌ WRONG: role="textbox" on a <div> with no keyboard/input handling -->
<div role="textbox" class="fake-input">Enter your name</div>
<!-- ✅ FIX B: Replace with a real <input> element -->
<label for="name">Your name</label>
<input type="text" id="name" name="name" placeholder="Enter your name">
<!-- ❌ WRONG: role="textbox" on a <p> element -->
<p role="textbox">Some editable text</p>
<!-- ✅ FIX C: If you need an editable region, use contenteditable + the permitted role -->
<!-- role="textbox" IS permitted on elements with contenteditable, like a <div> -->
<div role="textbox"
contenteditable="true"
aria-multiline="false"
aria-label="Your name">
</div>Common patterns: wrong role on the left, correct fix on the right (as comments).
Aria command nameModerate effort
Give every button, link, and menuitem a clear, accessible name so screen readers can announce what it does.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → Your current theme and use the theme editor or FTP/file manager to access theme templates.
- Locate the relevant .tpl (Smarty) file (e.g., themes/your-theme/templates/_partials/header.tpl or cart-summary.tpl).
- Find the unlabelled `<button>` or `<a>` and add `aria-label="Descriptive action"`. For SVG icons, add `aria-hidden="true"`.
- Clear the PrestaShop cache (Advanced Parameters → Performance → Clear cache) and verify on the front end with axe DevTools.
<!-- OPTION 1: aria-label on an icon-only button -->
<button type="button" aria-label="Open cart">
<svg aria-hidden="true" focusable="false" width="24" height="24" viewBox="0 0 24 24">
<path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z"/>
<line x1="3" y1="6" x2="21" y2="6"/>
<path d="M16 10a4 4 0 01-8 0"/>
</svg>
</button>
<!-- OPTION 2: visually-hidden span inside the button -->
<button type="button">
<svg aria-hidden="true" focusable="false" width="24" height="24" viewBox="0 0 24 24">
<path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z"/>
</svg>
<span class="sr-only">Open cart</span>
</button>
<!-- OPTION 3: aria-labelledby pointing to existing visible text -->
<h2 id="cart-heading">Your Cart</h2>
<button type="button" aria-labelledby="cart-heading">View</button>
<!-- OPTION 4: icon-only link with aria-label -->
<a href="/cart" aria-label="View shopping cart">
<img src="cart-icon.svg" alt="" role="presentation" />
</a>Apply one of these patterns to any button, link, or menuitem that contains only an icon or image.
Aria hidden focusModerate effort
Remove `aria-hidden="true"` from any element that contains focusable children (links, buttons, inputs), or remove those focusable children from the keyboard tab order so they cannot receive focus while hidden from assistive technology.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → your active theme.
- Navigate to your theme directory on the server (typically `themes/<your-theme>/templates/`) and search for `aria-hidden` using your file manager or SSH: `grep -r 'aria-hidden' themes/<your-theme>/`.
- Open the relevant `.tpl` (Smarty) file, locate the `aria-hidden="true"` on the parent container, and fix the markup so no focusable element is a descendant.
- Clear the PrestaShop cache (Advanced Parameters → Performance → Clear Cache) and test on the storefront with axe DevTools.
Aria input field nameModerate effort
Add a descriptive accessible name (aria-label, aria-labelledby, or a visible <label>) to every ARIA input field so assistive technologies can identify it.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → identify your active theme.
- Navigate to your theme folder at /themes/<your-theme>/templates/ and locate the offending template — common files: checkout/_partials/form-fields.tpl (checkout inputs), catalog/_partials/search-filters.tpl (filter inputs), layouts/search.tpl (search bar).
- In form-fields.tpl, find the <input> tag inside the loop and add: aria-label="{$field.label|escape:'html':'UTF-8'}" to associate the field label text directly on the input as well as using the visible <label>.
- Clear the PrestaShop cache: Advanced Parameters → Performance → Clear cache.
- Verify with WAVE or axe DevTools.
<label for="customer-email">Email address</label>
<input type="email" id="customer-email" name="email" autocomplete="email">Option 1 (preferred): Visible label associated via 'for' + 'id'. Use this whenever a visible label fits the design.
Aria required childrenModerate effort
Add the required child ARIA roles (e.g. `role="option"` inside `role="listbox"`, or `role="group"`/`role="option"` inside `role="combobox"`) so every composite ARIA widget contains the child roles the ARIA specification demands.
On PrestaShop
- Go to your PrestaShop back office → Design → Theme & Logo → (active theme). The template files live in /themes/[your-theme]/templates/.
- Use FTP/SFTP or your hosting file manager to open the relevant .tpl (Smarty) template (e.g. _partials/header.tpl, catalog/listing/product-list.tpl).
- Locate the parent ARIA role, check its children, and add the missing child role attributes or remove the disallowed tabindex from img/a elements.
- Clear PrestaShop's cache via Advanced Parameters → Performance → Clear Cache, then re-test with axe DevTools.
Aria required parentModerate effort
Wrap every ARIA role that requires a parent container (e.g. `menuitem`, `option`, `listitem`) inside the correct parent element carrying the matching ARIA role (e.g. `menu`/`menubar`, `listbox`, `list`).
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → select your theme → click 'Use this theme'.
- Via FTP/SFTP, navigate to `themes/<your-theme>/templates/` and find the template rendering the flagged widget (e.g., a product tabs template).
- Add the required ARIA parent role (e.g., `role="tablist"`) to the wrapper element surrounding the child role elements.
- Clear the Smarty cache in Advanced Parameters → Performance → Clear cache, then test the storefront.
<!-- INCORRECT: menuitem with no required parent -->
<nav>
<a href="/shop" role="menuitem">Shop</a>
<a href="/about" role="menuitem">About</a>
<a href="/contact" role="menuitem">Contact</a>
</nav>
<!-- CORRECT: menuitem children wrapped in role="menubar" -->
<nav>
<ul role="menubar">
<li role="none"><a href="/shop" role="menuitem">Shop</a></li>
<li role="none"><a href="/about" role="menuitem">About</a></li>
<li role="none"><a href="/contact" role="menuitem">Contact</a></li>
</ul>
</nav>
<!-- CORRECT: option children wrapped in role="listbox" -->
<div role="listbox" aria-label="Select a size">
<div role="option" aria-selected="false">Small</div>
<div role="option" aria-selected="true">Medium</div>
<div role="option" aria-selected="false">Large</div>
</div>
<!-- CORRECT: tab children wrapped in role="tablist" -->
<div role="tablist" aria-label="Product details">
<button role="tab" aria-selected="true" aria-controls="panel-desc">Description</button>
<button role="tab" aria-selected="false" aria-controls="panel-reviews">Reviews</button>
</div>
<div id="panel-desc" role="tabpanel">Description content here.</div>
<div id="panel-reviews" role="tabpanel" hidden>Reviews content here.</div>Correct pattern: child roles (menuitem) wrapped in the required parent role (menu/menubar). Apply the same principle to other role pairs.
Aria valid attr valueModerate effort
Audit every ARIA attribute on your pages and correct any that have invalid or mismatched values so assistive technologies can interpret them correctly.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → click 'Use this theme' or navigate to your active theme's files via FTP.
- In your active child theme folder (themes/<your-theme>/), search all .tpl (Smarty template) files for ARIA attributes using a code editor.
- Correct ID-reference mismatches and invalid keyword values directly in the template files.
- Clear the theme cache via Design → Theme & Logo → Advanced Settings → Clear cache, then retest with axe DevTools.
<!-- WRONG: aria-expanded with a number instead of boolean string -->
<button aria-expanded="1">Menu</button>
<!-- CORRECT: must be exactly the string "true" or "false" -->
<button aria-expanded="false">Menu</button>
<!-- WRONG: aria-describedby pointing to an ID that doesn't exist -->
<input type="email" aria-describedby="email-hint" />
<!-- (no element with id="email-hint" on the page) -->
<!-- CORRECT: the referenced element must exist in the DOM -->
<input type="email" aria-describedby="email-hint" />
<p id="email-hint">We'll never share your email with anyone.</p>
<!-- WRONG: aria-checked with a non-standard value -->
<div role="checkbox" aria-checked="yes">Subscribe</div>
<!-- CORRECT: aria-checked accepts "true", "false", or "mixed" -->
<div role="checkbox" aria-checked="false">Subscribe</div>
<!-- WRONG: aria-hidden with an invalid value -->
<span aria-hidden="hidden">decorative icon</span>
<!-- CORRECT: aria-hidden accepts only "true" or "false" -->
<span aria-hidden="true">decorative icon</span>Correct examples for the most commonly mis-valued ARIA attributes. Copy the pattern that matches your flagged element.
Button nameModerate effort
Add a descriptive, screen-reader-accessible name to every button on your site so assistive technology users know what each button does.
On PrestaShop
- In your theme directory (themes/<your-theme>/templates/), locate the relevant .tpl file (e.g. _partials/header.tpl, catalog/product.tpl).
- Find the <button> element and add aria-label="{l s='Descriptive name' d='Shop.Theme'}" using PrestaShop's translation helper.
- Clear the PrestaShop cache under Advanced Parameters → Performance → Clear Cache, then verify with axe DevTools.
<!-- PATTERN 1: Icon-only button — add aria-label directly on the button -->
<button type="button" aria-label="Open search">
<svg aria-hidden="true" focusable="false" width="24" height="24" viewBox="0 0 24 24">
<path d="M21 21l-4.35-4.35M17 11A6 6 0 1 1 5 11a6 6 0 0 1 12 0z"
stroke="currentColor" stroke-width="2" fill="none"/>
</svg>
</button>
<!-- PATTERN 2: Button with an <img> icon — give the img a descriptive alt -->
<button type="button">
<img src="/icons/cart.svg" alt="View shopping cart" width="24" height="24">
</button>
<!-- PATTERN 3: Button labelled by visible text elsewhere on the page -->
<h2 id="newsletter-heading">Subscribe to our newsletter</h2>
<!-- ... form fields ... -->
<button type="submit" aria-labelledby="newsletter-heading">Subscribe</button>
<!-- PATTERN 4: Visible text button — just ensure the text is real HTML text -->
<button type="button">Add to Cart</button>
<!-- PATTERN 5: Quantity stepper buttons -->
<button type="button" aria-label="Decrease quantity">−</button>
<input type="number" value="1" aria-label="Product quantity">
<button type="button" aria-label="Increase quantity">+</button>Use one of these patterns depending on your button's content. Apply the correct pattern to every icon-only or ambiguous button on your site.
Color contrastQuick win
Increase the contrast ratio between your text color and its background color to meet the WCAG 4.5:1 minimum, so all users — including those with low vision — can read your content clearly.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → Theme Configurator (if available) and look for text color settings.
- Otherwise, use FTP/file manager to open your active theme folder (themes/<your-theme>/assets/css/) and find the relevant CSS file. Search for the failing hex value and replace it.
- Clear the PrestaShop cache under Advanced Parameters → Performance → Clear Cache.
/* BEFORE (fails WCAG 1.4.3 — ratio 4.47:1 on white) */
.secondary-text {
color: #777777;
}
/* AFTER (passes WCAG 1.4.3 — ratio ~7.0:1 on white) */
.secondary-text {
color: #595959;
}Replace the failing color with one that achieves 4.5:1+ contrast on white (#ffffff). #595959 achieves ~7.0:1 on white.
Definition listModerate effort
Ensure every `<dl>` (definition list) element contains only properly nested `<dt>` and `<dd>` children, with no stray text, divs, or other elements placed directly inside it.
On PrestaShop
- Via FTP or your hosting file manager, navigate to themes/<your-theme>/templates/ and search for files containing `<dl`.
- Common locations: catalog/product.tpl (product details), checkout/ templates.
- Edit the Smarty (.tpl) template file to correct the `<dl>` structure, ensuring only `<dt>` and `<dd>` (or `<div>` wrappers) are direct children.
- Clear the PrestaShop cache under Advanced Parameters → Performance → Clear cache, then verify with axe.
<!-- ✅ CORRECT: only <dt> and <dd> as direct children -->
<dl>
<dt>Material</dt>
<dd>100% Organic Cotton</dd>
<dt>Weight</dt>
<dd>180 GSM</dd>
<dt>Care Instructions</dt>
<dd>Machine wash cold</dd>
<dd>Tumble dry low</dd>
</dl>
<!-- ✅ ALSO CORRECT: <div> wrappers for layout/styling -->
<dl>
<div>
<dt>Material</dt>
<dd>100% Organic Cotton</dd>
</div>
<div>
<dt>Weight</dt>
<dd>180 GSM</dd>
</div>
</dl>
<!-- ❌ WRONG: stray <p>, <span>, or plain text directly inside <dl> -->
<!-- <dl>
<p>Product Details</p>
<dt>Material</dt>
<dd>Cotton</dd>
</dl> -->Correct `<dl>` structure — use this pattern for all product specs, FAQs, and paired term/value content.
Document titleQuick win
Add a descriptive, non-empty `<title>` element to every page so browsers, screen readers, and search engines can identify it.
On PrestaShop
- For individual pages: Back Office → Catalog → Products → select a product → 'SEO' tab → 'Meta title' field.
- For CMS pages: Design → Pages → select a page → 'SEO' tab → 'Meta title'.
- For category pages: Catalog → Categories → select a category → 'SEO' tab → 'Meta title'.
- For the homepage and global defaults: Shop Parameters → Traffic & SEO → 'Meta title' for the home page row.
- Ensure the `{$meta_title}` Smarty variable is present in `<title>{$meta_title}</title>` inside your theme's `_partials/head.tpl` file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Women's Running Shoes – Acme Sports</title>
</head>
<body>
<!-- page content -->
</body>
</html>Place this inside the <head> section of every HTML page. Replace the text with the actual page and store name.
Empty table headerQuick win
Add visible or screen-reader-accessible text to every empty `<th>` table header cell so assistive technology users can understand what each column or row represents.
On PrestaShop
- In your hosting file manager or via FTP, navigate to themes/<your-theme>/templates/ and search for .tpl files containing `<table>` and `<th>` markup.
- Edit the relevant .tpl file to add visible text or an `aria-label` attribute to any empty `<th>` elements.
- Clear the PrestaShop cache via Back Office → Advanced Parameters → Performance → Clear Cache.
- Verify the fix with axe DevTools.
<!-- OPTION 1: Add visible text (best for most cases) -->
<table>
<thead>
<tr>
<th>Feature</th>
<th>Basic Plan</th>
<th>Pro Plan</th>
</tr>
</thead>
<tbody>
<tr>
<td>Storage</td>
<td>5 GB</td>
<td>50 GB</td>
</tr>
</tbody>
</table>
<!-- OPTION 2: Visually hidden text (for top-left corner cells) -->
<style>
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>
<table>
<thead>
<tr>
<th><span class="sr-only">Feature name</span></th>
<th>Basic Plan</th>
<th>Pro Plan</th>
</tr>
</thead>
</table>
<!-- OPTION 3: aria-label directly on the th element -->
<table>
<thead>
<tr>
<th aria-label="Feature name"></th>
<th>Basic Plan</th>
<th>Pro Plan</th>
</tr>
</thead>
</table>Option 1 — visible text in the header cell (preferred). Option 2 — visually hidden text via a span. Option 3 — aria-label on the th element.
Focus not visibleQuick win
Add a clearly visible focus indicator to every interactive element so keyboard users can always see which element is currently selected.
On PrestaShop
- Navigate to your active theme folder: themes/<your-theme>/assets/css/.
- Open custom.css or theme.css and search for 'outline: none'.
- Remove or override those rules and add the universal focus CSS from the snippet below.
- If you want to avoid editing theme files, go to Back Office → Design → Theme & Logo → Add CSS and inject the rule there.
- Clear the cache (Advanced Parameters → Performance → Clear cache) and tab-test your storefront.
/* =====================================================
Universal visible focus indicator — WCAG 2.4.7
Add to your global/main stylesheet.
Adjust the color values to match your brand palette
while keeping contrast ≥ 3:1 against the background.
===================================================== */
/* Modern browsers: :focus-visible only shows the ring
for keyboard navigation, not mouse clicks. */
*:focus-visible {
outline: 3px solid #005FCC; /* high-contrast blue */
outline-offset: 2px; /* gap between element and ring */
border-radius: 2px; /* optional: soften corners */
}
/* Fallback for older browsers that don't support
:focus-visible — applies to all focus events. */
*:focus {
outline: 3px solid #005FCC;
outline-offset: 2px;
}
/* If you must remove the outline for mouse users in
older browsers, do it ONLY when :focus-visible is
NOT matched — never remove it globally. */
*:focus:not(:focus-visible) {
outline: none;
}Add to your main global stylesheet. This is the safest, most universal fix.
Frame titleQuick win
Add a descriptive `title` attribute to every `<iframe>` and `<frame>` element on your store so screen-reader users know what each embedded frame contains before entering it.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → click Your current theme → then Theme Editor, or access theme files via FTP at themes/<your-theme>/templates/.
- Search template .tpl files for '<iframe' and add title="…" to each one.
- For iframes inside PrestaShop modules, override the module template: copy the module's template to themes/<your-theme>/modules/<module-name>/ and edit it there to add the title attribute.
- For iframes in CMS pages (Content → Pages), use the TinyMCE HTML source editor to add the title attribute directly.
<!-- Good: descriptive title -->
<iframe
src="https://www.google.com/maps/embed?pb=..."
title="Google Map showing our store location at 123 Main St"
width="600"
height="450"
loading="lazy"
referrerpolicy="no-referrer-when-downgrade"
></iframe>
<!-- Good: product video embed -->
<iframe
src="https://www.youtube.com/embed/VIDEO_ID"
title="Product demo video: how to use the XYZ Widget"
width="560"
height="315"
allowfullscreen
></iframe>
<!-- Good: decorative / empty frame — hide from assistive tech -->
<iframe
src="blank.html"
title=""
aria-hidden="true"
tabindex="-1"
></iframe>Add a descriptive title attribute directly to any iframe element.
Html has langQuick win
Add a valid `lang` attribute to the `<html>` element so browsers, screen readers, and search engines know what language your page is written in.
On PrestaShop
- In your PrestaShop admin, go to International → Translations → Languages to confirm your default language is set correctly.
- Open your active theme's layout file — typically themes/[your-theme]/templates/layouts/layout-full-width.tpl or layout-both-columns.tpl.
- Find the `<html>` tag and ensure it reads `<html lang="{$language.iso_code}">` — PrestaShop provides the `{$language.iso_code}` Smarty variable for this.
- Save and verify via View Source.
<!DOCTYPE html>
<html lang="{$language.iso_code}">
<head>
<meta charset="utf-8">
<title>{$page.meta.title}</title>
{block name='head'}{/block}
</head>
<body>
{block name='content'}{/block}
</body>
</html>Place this in your theme's layout Smarty template (e.g., layout-full-width.tpl). The {$language.iso_code} variable outputs the current language code.
Html lang validQuick win
Add a valid BCP 47 language code to the `lang` attribute on your site's `<html>` element so browsers and assistive technologies know what language your content is written in.
On PrestaShop
- PrestaShop derives the `lang` attribute from the active store language. Go to Back Office → International → Translations → Languages and confirm the correct language is installed and active.
- In your theme's `templates/_partials/head.tpl` (or the root layout, often `templates/layouts/layout-full-width.tpl`), find the `<html` tag and ensure it uses the Smarty variable: `<html lang="{$language.iso_code}">`.
- Clear your theme cache via Back Office → Design → Theme & Logo → (Advanced Customization) or via Advanced Parameters → Performance → Clear Cache.
- Verify the rendered page source shows a valid ISO language code on the `<html>` tag.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Page Title</title>
</head>
<body>
<!-- page content -->
</body>
</html>Place this as the very first tag in your HTML document. Replace 'en' with your actual BCP 47 language code.
Image altModerate effort
Add descriptive alt text to every meaningful image, and mark purely decorative images with an empty alt attribute or role="presentation".
On PrestaShop
- Product images: Back Office → Catalog → Products → edit a product → Images tab → click an image → fill in the 'Caption' field (this becomes the alt attribute) for each active language → Save.
- CMS page images: Back Office → Design → Pages → edit a CMS page → in the TinyMCE editor, right-click an image → Image Properties → fill in the 'Alternative description (alt text)' field.
- Theme template files: Edit the relevant `.tpl` file (e.g., `themes/<your-theme>/templates/catalog/_partials/product-cover-thumbnails.tpl`) and ensure `{$image.legend}` or a similar variable is output as the `alt` attribute.
<!-- Meaningful image: describe the content -->
<img src="red-leather-bag.jpg" alt="Red leather crossbody bag with gold clasp" width="600" height="400">
<!-- Image used as a link: describe the destination/action -->
<a href="/sale">
<img src="summer-sale-banner.jpg" alt="Shop our summer sale — up to 40% off">
</a>
<!-- Decorative image: empty alt, screen readers skip it -->
<img src="decorative-swoosh.svg" alt="" role="presentation">
<!-- WRONG — never omit alt entirely on an <img> -->
<!-- <img src="product.jpg"> -->
<!-- WRONG — never use filename or generic words -->
<!-- <img src="IMG_4821.jpg" alt="image"> -->Correct patterns for meaningful, linked, and decorative images.
Image redundant altQuick win
Remove or clear the alt text on images whose description is already provided by visible text immediately surrounding them, so screen readers don't read the same information twice.
On PrestaShop
- For product images: go to Admin → Catalog → Products → select the product → Images tab → click the image → clear the 'Caption' field (used as alt text) for each language → Save.
- For CMS page images: Admin → Design → Pages → select the page → in the TinyMCE editor, click the image → Image Properties → clear the 'Alternative Description' field → OK → Save.
- For theme template images: edit the relevant .tpl file in /themes/<your-theme>/ → find the <img> tag → set alt="" → clear template cache under Admin → Advanced Parameters → Performance.
<!-- CORRECT: image inside a link that also has visible text -->
<a href="/products/red-sneakers">
<img src="red-sneakers.jpg" alt="" width="300" height="300">
<span>Red Sneakers</span>
</a>
<!-- INCORRECT: alt repeats the visible link text -->
<a href="/products/red-sneakers">
<img src="red-sneakers.jpg" alt="Red Sneakers" width="300" height="300">
<span>Red Sneakers</span>
</a>
<!-- INCORRECT: alt attribute missing entirely (screen reader reads filename) -->
<a href="/products/red-sneakers">
<img src="red-sneakers.jpg" width="300" height="300">
<span>Red Sneakers</span>
</a>Use alt="" (empty string) when the image is inside or adjacent to visible text that already describes it. Never omit the alt attribute entirely.
Input button nameQuick win
Add a visible label or accessible name to every input button so assistive technologies can announce what the button does.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → open your active theme in FTP or the theme editor.
- Search `.tpl` (Smarty) template files for `<input type="submit"` or `<input type="button"` with an empty `value` attribute.
- Common files: `themes/<your-theme>/templates/checkout/`, `templates/catalog/`, and `templates/contact.tpl`.
- Add a descriptive `value` attribute, e.g. `value="{l s='Add to cart' d='Shop.Theme.Actions'}"` to use PrestaShop's localisation helpers.
- Clear the Smarty cache under Advanced Parameters → Performance → Clear cache, then test.
{{* BEFORE (broken) *}}
{{* <input type="submit" value=""> *}}
{{* AFTER (fixed): translatable label *}}
<input
type="submit"
value="{l s='Add to cart' d='Shop.Theme.Actions'}"
class="btn btn-primary add-to-cart"
>In a Smarty .tpl template, use the {l} localisation tag to provide a translated, non-empty value.
LabelQuick win
Add a visible or programmatic label to every form input so assistive technologies can identify it for users.
On PrestaShop
- In your theme folder (typically `themes/<your-theme>/templates/`), open the relevant template — e.g. `checkout/_partials/steps/addresses.tpl`, `contact/contact.tpl`, or `_partials/header.tpl` for search.
- Add `<label for='field_id'>{l s='Descriptive text' d='Shop.Theme.Global'}</label>` before the corresponding `<input id='field_id'>` tag.
- For search, add `aria-label='{l s='Search' d='Shop.Theme.Global'}'` to the `<input>` element in `_partials/header.tpl`.
- Clear the PrestaShop cache via Advanced Parameters → Performance → Clear cache, then verify.
<!-- Method 1: Explicit label (recommended) -->
<label for="user-email">Email address</label>
<input type="email" id="user-email" name="email" autocomplete="email">
<!-- Method 2: Implicit (wrapped) label -->
<label>
Email address
<input type="email" name="email" autocomplete="email">
</label>
<!-- Method 3: aria-label (use when no visible label is desired, e.g. icon-only search) -->
<input type="search" name="q" aria-label="Search products">
<!-- Method 4: aria-labelledby (point to existing visible text on the page) -->
<h2 id="newsletter-heading">Subscribe to our newsletter</h2>
<input type="email" name="subscribe" aria-labelledby="newsletter-heading">
<!-- Visually hidden label (visible to screen readers, hidden on screen) -->
<style>
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>
<label for="search-input" class="visually-hidden">Search products</label>
<input type="search" id="search-input" name="q" placeholder="Search...">Preferred method: explicit label linked by for/id. Place immediately before the input.
Label title onlyModerate effort
Add a visible text label to every form field so it is not labeled only by a hidden tooltip (title attribute) or supplementary description (aria-describedby).
On PrestaShop
- In your PrestaShop admin, go to Design → Theme & Logo → and use FTP to access your active theme's template files under `themes/YOUR_THEME/templates/`.
- Locate the relevant `.tpl` file — common files are `customer/authentication.tpl` (login/register), `checkout/checkout.tpl` (checkout fields), and `_partials/header.tpl` (search).
- For each `<input>` that has only a `title` attribute, add `<label for="FIELD_ID">Visible Label</label>` before the input and add `id="FIELD_ID"` to the `<input>` tag. For icon-only search bars, add `aria-label="Search"` to the input.
- Clear the PrestaShop cache (Advanced Parameters → Performance → Clear cache) and verify with an accessibility scanner.
<!-- CORRECT: Visible label paired to input -->
<div class="form-field">
<label for="customer-email">Email address</label>
<input
type="email"
id="customer-email"
name="email"
autocomplete="email"
/>
</div>
<!-- CORRECT: Visually-hidden label (readable by screen readers, invisible to sighted users) -->
<style>
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>
<div class="form-field">
<label for="site-search" class="visually-hidden">Search the store</label>
<input
type="search"
id="site-search"
name="q"
placeholder="Search..."
/>
</div>
<!-- CORRECT: aria-label as alternative when no <label> element is possible -->
<input
type="search"
name="q"
aria-label="Search the store"
placeholder="Search..."
/>
<!-- WRONG: title only — do NOT rely on this -->
<!-- <input type="search" name="q" title="Search the store" /> -->
<!-- WRONG: placeholder only — do NOT rely on this -->
<!-- <input type="email" name="email" placeholder="Email address" /> -->The correct pattern: a visible <label> paired to an input via matching for/id attributes.
Landmark banner is top levelModerate effort
Move your site's `<header>` element (or `role="banner"` landmark) so it is a direct child of `<body>`, not nested inside another landmark region.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → click 'Edit theme' or access files via FTP under `themes/<your-theme>/templates/`.
- Open `layouts/layout-full-width.tpl` or `layouts/layout-left-column.tpl` (whichever your theme uses) — this is the master page template.
- Locate the `{block name='header'}` or the `<header>` element and confirm it is rendered as a direct child of `<body>`, not inside a `{block name='main'}` or any element with a landmark role.
- If the header is nested inside a landmark, restructure the template blocks so the header comes before and outside the main content landmark.
- Clear the PrestaShop cache (Advanced Parameters → Performance → Clear cache) and verify with axe DevTools.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Store</title>
</head>
<body>
<!-- CORRECT: <header> is a direct child of <body> -->
<header>
<a href="/"><img src="/logo.svg" alt="My Store home"></a>
<nav aria-label="Primary">
<ul>
<li><a href="/shop">Shop</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
</header>
<main id="main-content">
<!-- page content here -->
</main>
<footer>
<!-- footer content here -->
</footer>
</body>
</html>Correct top-level structure: <header> is a direct child of <body>, not nested in any landmark.
Landmark contentinfo is top levelQuick win
Move your site's footer element (or role="contentinfo") so it is a direct child of the page body, not nested inside another landmark region.
On PrestaShop
- In your PrestaShop admin, go to Design → Theme & Logo → select your active theme → click 'Configure'.
- Via FTP or your hosting file manager, navigate to `themes/<your-theme>/templates/_partials/footer.tpl` (or `footer.tpl` in the root templates folder).
- Open the file and check that the `<footer>` tag is not wrapped inside another landmark element within this file.
- Also check `themes/<your-theme>/templates/layouts/layout-full-width.tpl` (and other layout files) to confirm that the `{block name='footer'}` include appears after the closing `</main>` tag, not inside it.
- Save changes, clear the PrestaShop cache (Advanced Parameters → Performance → Clear Cache), and validate with axe DevTools.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Store Page</title>
</head>
<body>
<header>
<!-- Site logo, main navigation -->
</header>
<nav aria-label="Main navigation">
<!-- Primary nav links -->
</nav>
<main>
<!-- All primary page content goes here -->
<h1>Page Title</h1>
<p>Page content...</p>
</main>
<!-- CORRECT: <footer> is a direct child of <body>, after </main> -->
<footer>
<p>© 2024 My Store. All rights reserved.</p>
<nav aria-label="Footer navigation">
<a href="/privacy">Privacy Policy</a>
<a href="/contact">Contact</a>
</nav>
</footer>
</body>
</html>Correct top-level landmark structure — <footer> is a direct child of <body>, not nested inside <main> or any other landmark.
Landmark no duplicate bannerModerate effort
Ensure your page has only one banner landmark (one `<header>` or `role="banner"` element that is not nested inside another landmark), so screen reader users can navigate your site reliably.
On PrestaShop
- Via FTP or your hosting file manager, go to themes/<your-theme>/templates/layouts/ and open layout-full-width.tpl (or whichever layout your store uses).
- Search for '<header' and 'role="banner"'. Ensure only one top-level <header> exists for the site header.
- Change any duplicate <header> elements (in module template files under modules/<module-name>/views/templates/) to <div> and remove role="banner".
- Clear PrestaShop's cache: Back Office → Advanced Parameters → Performance → Clear cache.
- Verify with axe DevTools.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Store Page</title>
</head>
<body>
<!-- CORRECT: Single top-level <header> = one banner landmark -->
<header>
<a href="/"><img src="logo.svg" alt="Acme Store"></a>
<nav aria-label="Main navigation">
<ul>
<li><a href="/shop">Shop</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
</header>
<main>
<article>
<!-- CORRECT: <header> nested inside <article> is NOT a banner landmark -->
<header>
<h1>Product Name</h1>
<p>Posted: <time datetime="2024-01-01">January 1, 2024</time></p>
</header>
<p>Product description goes here.</p>
</article>
<!-- CORRECT: promotional strip uses <div>, not <header> -->
<div class="promo-bar" role="region" aria-label="Promotional offer">
<p>Free shipping on orders over $50!</p>
</div>
</main>
<footer>
<p>© 2024 Acme Store</p>
</footer>
</body>
</html>Correct page structure: one top-level <header> as the banner, inner content sections use <div> or are wrapped in a sectioning element.
Landmark no duplicate mainQuick win
Find and remove every duplicate `<main>` element or `role="main"` attribute so that exactly one main landmark exists on each page.
On PrestaShop
- Navigate to your active theme folder at /themes/<your-theme>/templates/. Open _layouts/layout-full-width.tpl (and other layout .tpl files).
- Search for '<main' and 'role="main"'. Keep only the one wrapping the central content block ({block name='content'}) and change any others to '<div>'.
- Also check header.tpl and footer.tpl in _partials/ for stray main landmark usage.
- Clear the PrestaShop cache under Advanced Parameters → Performance → Clear cache, then verify with the axe extension.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Page Title</title>
</head>
<body>
<header>
<nav aria-label="Primary navigation">
<!-- site-wide navigation -->
</nav>
</header>
<!-- ✅ Only ONE <main> element per page -->
<main id="main-content">
<h1>Page Heading</h1>
<!-- primary page content goes here -->
</main>
<footer>
<!-- footer content -->
</footer>
</body>
</html>Correct page structure: exactly one <main> element wrapping the primary content, with header and footer outside it.
Landmark one mainQuick win
Add a single `<main>` landmark element (or `role="main"`) to every page so assistive technologies can identify and navigate directly to the primary content.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → click 'Use FTP to add your theme' or edit theme files directly.
- Open your active theme's `templates/_partials/header.tpl` and `templates/index.tpl` (or the relevant page template).
- Locate the `<div id="main">` or `<div id="wrapper">` that wraps the main content block and change its tag to `<main id="main">` with the closing `</main>`.
- Save, clear the PrestaShop cache under Advanced Parameters → Performance → Clear cache, and verify with axe DevTools.
<!-- Skip link at the very top of <body> -->
<a href="#main-content" class="skip-link">Skip to main content</a>
<header><!-- site header / nav here --></header>
<main id="main-content">
<!-- ALL primary page content goes here:
product details, blog post, category grid, etc. -->
</main>
<footer><!-- site footer here --></footer>Place this landmark element around all unique page content, after the <header> and <nav> and before the <footer>. Use exactly one per page.
Link nameQuick win
Add a descriptive, screen-reader-accessible text label to every link on your store so assistive technology can announce its purpose.
On PrestaShop
- Go to PrestaShop Admin → Design → Theme & Logo → select your theme → click 'Edit' or access via FTP.
- Common problem templates: themes/[yourtheme]/templates/_partials/header.tpl (cart/search icons) and themes/[yourtheme]/templates/catalog/_partials/miniature.tpl (product card links).
- Use a child theme override (copy the file to themes/[yourtheme]/templates/) so core updates don't overwrite changes.
- For icon links, add aria-label: <a href='{$urls.pages.cart}' aria-label='{l s='View cart' d='Shop.Theme.Global'}'>.
- Clear the Smarty cache (Advanced Parameters → Performance → Clear cache) after saving.
Meta refreshQuick win
Remove or disable the `<meta http-equiv="refresh">` tag that forces an automatic page reload, or replace any redirect use-case with a server-side redirect instead.
On PrestaShop
- Access your server via FTP/SSH and open your active theme folder at `/themes/<your-theme>/`.
- Open `templates/_partials/head.tpl` (or `header.tpl` for older themes) and search for `http-equiv`. Remove the matching line.
- If injected by a module, go to Modules → Module Manager, identify the module, and disable the meta-refresh feature in its configuration.
- For redirects, configure a `.htaccess` 301 redirect or use a dedicated redirect module from the PrestaShop marketplace.
- Clear the PrestaShop cache under Advanced Parameters → Performance → Clear Cache, then re-scan.
<!-- REMOVE — self-refresh every 30 seconds (WCAG fail) -->
<!-- <meta http-equiv="refresh" content="30"> -->
<!-- REMOVE — redirect to another URL after 5 seconds (WCAG fail) -->
<!-- <meta http-equiv="refresh" content="5; url=https://example.com/new-page"> -->
<!-- ALLOWED — 0-second instant redirect (no time limit imposed on user) -->
<!-- Only use this as a fallback alongside a server-side redirect -->
<meta http-equiv="refresh" content="0; url=https://example.com/new-page">REMOVE this tag from your page's <head>. Both forms below are problematic (self-refresh and timed redirect).
Meta viewportQuick win
Remove `user-scalable=no` (and any `maximum-scale` below 5) from your site's `<meta name="viewport">` tag so visitors can pinch-to-zoom on mobile devices.
On PrestaShop
- Via FTP, navigate to themes/<your-theme>/templates/_partials/head.tpl (path may vary by theme version).
- Search for 'viewport' and edit the <meta name="viewport"> line to remove `user-scalable=no`.
- Save and upload. Clear the PrestaShop cache via Admin → Advanced Parameters → Performance → Clear cache.
<meta name="viewport" content="width=device-width, initial-scale=1">Place this in the <head> of every HTML page. This is the correct, accessibility-compliant viewport tag.
Meta viewport largeQuick win
Remove or raise the `maximum-scale` value in your meta viewport tag so users can pinch-zoom on mobile devices.
On PrestaShop
- Via FTP/SFTP or your hosting file manager, navigate to your active theme folder: themes/<your-theme>/templates/_partials/ or themes/<your-theme>/templates/.
- Open 'head.tpl' and search for 'viewport'.
- Edit the <meta name="viewport"> tag to remove maximum-scale=1 or user-scalable=no.
- Save the file and clear PrestaShop's cache under Advanced Parameters → Performance → Clear Cache.
<meta name="viewport" content="width=device-width, initial-scale=1">In themes/<your-theme>/templates/_partials/head.tpl, replace the existing viewport meta tag with this line.
Nested interactiveModerate effort
Remove or restructure nested interactive elements so that no focusable control (button, link, input, etc.) is placed inside another interactive control, and never rely on tabindex="-1" or aria-hidden="true" to hide an inner interactive element from assistive technologies.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → [your theme] and use FTP or the Theme Editor to locate the affected template file (commonly themes/<yourtheme>/templates/catalog/_partials/miniatures/product.tpl or checkout/).
- Open the .tpl (Smarty) file and search for nested interactive elements.
- Refactor: replace one of the two interactive elements with a `<span>` or `<div>`, preserving visual styles.
- Clear PrestaShop's Smarty cache under Advanced Parameters → Performance → Clear Cache, then verify the fix.
<!-- ❌ WRONG: button nested inside a link (invalid HTML, unpredictable in screen readers) -->
<a href="/products/widget">
<button type="button">View Product</button>
</a>
<!-- ✅ CORRECT: single interactive element styled as a button -->
<a href="/products/widget" class="btn btn-primary">View Product</a>
<!-- ❌ WRONG: link nested inside a button -->
<button type="button">
<a href="/cart">Add to Cart</a>
</button>
<!-- ✅ CORRECT: use only the element that matches the action -->
<!-- If it navigates → use <a>; if it performs an action on the page → use <button> -->
<a href="/cart" class="btn btn-primary">Add to Cart</a>
<!-- ❌ WRONG: relying on tabindex or aria-hidden to suppress inner element -->
<a href="/products/widget">
<button type="button" tabindex="-1" aria-hidden="true">View Product</button>
</a>
<!-- ✅ CORRECT: icons and decorative content inside interactive elements must be non-interactive -->
<button type="button">
<svg aria-hidden="true" focusable="false" width="20" height="20">
<use href="#icon-cart"></use>
</svg>
<span>Add to Cart</span>
</button>The WRONG pattern and the correct fix side by side.
RegionModerate effort
Wrap all visible page content inside HTML landmark regions (e.g. `<header>`, `<main>`, `<nav>`, `<footer>`) so screen-reader users can navigate the page by section.
On PrestaShop
- Edit your active theme's template files located in 'themes/<your-theme>/templates/'. Open '_partials/header.tpl' and ensure the header output is wrapped in `<header role='banner'>` and nav in `<nav aria-label='Main navigation'>`.
- Open '_partials/footer.tpl' and wrap all footer content in `<footer role='contentinfo'>`.
- Open 'index.tpl', 'catalog/product.tpl', and 'catalog/listing/product-list.tpl' — confirm the primary content is inside `<main id='main' role='main'>`.
- Clear the PrestaShop cache from Advanced Parameters → Performance → Clear cache, then test with the axe extension.
Role img altModerate effort
Add a meaningful aria-label (or aria-labelledby) to every element with role="img" so screen readers can announce what the image represents.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → your active theme.
- Use FTP or your hosting file manager to navigate to /themes/[your-theme]/templates/ and search for role="img" across .tpl (Smarty) files.
- Add aria-label="{l s='Description' d='Shop.Theme'}" to each flagged element, using PrestaShop's translation function so labels are translatable.
- Clear the Smarty cache under Advanced Parameters → Performance → Clear cache.
- Validate with the axe browser extension.
<!-- Pattern 1: Inline element (icon font, emoji, span) -->
<span role="img" aria-label="Free shipping available">🚚</span>
<!-- Pattern 2: SVG element — use <title> + aria-labelledby (most robust) -->
<svg role="img" aria-labelledby="icon-free-shipping" width="24" height="24" xmlns="http://www.w3.org/2000/svg">
<title id="icon-free-shipping">Free shipping available</title>
<use href="#icon-truck"></use>
</svg>
<!-- Pattern 3: SVG with aria-label only (simpler, slightly less support) -->
<svg role="img" aria-label="4.5 out of 5 stars" width="120" height="24" xmlns="http://www.w3.org/2000/svg">
<!-- star paths here -->
</svg>
<!-- Pattern 4: div/section used as a decorative image — HIDE from screen readers -->
<div role="presentation" aria-hidden="true" class="decorative-banner"></div>
<!-- Pattern 5: referencing an existing visible caption -->
<figure>
<div role="img" aria-labelledby="badge-caption" class="trust-badge"></div>
<figcaption id="badge-caption">Verified Secure Checkout</figcaption>
</figure>Apply to any HTML element using role="img" — choose the pattern that matches your element type.
Select nameQuick win
Add a visible or programmatic label to every `<select>` dropdown so assistive technologies can announce its purpose to users.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → your active theme. Template files are in '/themes/YOUR-THEME/templates/'.
- Find the template rendering the unlabelled <select> — common files: 'catalog/product.tpl' (variant selectors), 'checkout/checkout.tpl' (address country/state), '_partials/form-fields.tpl' (generic form fields).
- Add a <label for="MATCHING-ID">{l s='Descriptive text' d='Shop.Theme'}</label> before the <select> tag, or add aria-label="{l s='Descriptive text' d='Shop.Theme'}" on the element itself.
- Clear the PrestaShop cache via Advanced Parameters → Performance → Clear cache, then verify.
<!-- Method 1: Visible label (preferred) -->
<label for="sort-order">Sort by</label>
<select id="sort-order" name="sort">
<option value="price-asc">Price: Low to High</option>
<option value="price-desc">Price: High to Low</option>
<option value="newest">Newest</option>
</select>
<!-- Method 2: Visually hidden label (when visible label breaks design) -->
<style>
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>
<label for="sort-order" class="visually-hidden">Sort by</label>
<select id="sort-order" name="sort">
<option value="price-asc">Price: Low to High</option>
<option value="price-desc">Price: High to Low</option>
<option value="newest">Newest</option>
</select>
<!-- Method 3: aria-label (last resort, no visible label possible) -->
<select aria-label="Sort by" name="sort">
<option value="price-asc">Price: Low to High</option>
<option value="price-desc">Price: High to Low</option>
<option value="newest">Newest</option>
</select>
<!-- Method 4: Wrapping label (implicit association — no for/id needed) -->
<label>
Sort by
<select name="sort">
<option value="price-asc">Price: Low to High</option>
<option value="price-desc">Price: High to Low</option>
<option value="newest">Newest</option>
</select>
</label>The gold-standard approach: a visible <label> linked to the <select> via matching for/id attributes.
Skip linkQuick win
Add a valid, focusable target element (with a matching `id`) for every "Skip to main content" link so keyboard and assistive-technology users can bypass repeated navigation.
On PrestaShop
- In your PrestaShop back office, go to Design → Theme & Logo → click 'Use this theme' for your active theme, then access theme files via FTP at `themes/<your-theme>/templates/layouts/layout-full-width.tpl` (or `layout-both-columns.tpl`).
- Immediately after `{block name='hook_after_body_open_tag'}{/block}`, add: `<a class="skip-link" href="#main">Skip to main content</a>`.
- Locate the `<main>` element in the layout template and add `id="main" tabindex="-1"`.
- Add the skip link CSS to `themes/<your-theme>/assets/css/custom.css` (see snippet below).
- Clear the PrestaShop cache (Advanced Parameters → Performance → Clear cache) and test with keyboard navigation.
TabindexModerate effort
Remove or replace all positive tabindex values (tabindex="1" or higher) on your page elements, using tabindex="0" for focusable elements and tabindex="-1" only when programmatic focus is needed.
On PrestaShop
- Connect via FTP/SFTP or use the Admin File Manager. Navigate to themes/<your-theme>/templates/ and search all .tpl (Smarty) files for 'tabindex'.
- Edit the offending .tpl file and remove or set the tabindex to 0. Always work in a child theme or custom theme to avoid losing changes on updates.
- Also check any installed modules (modules/<module-name>/views/templates/) for positive tabindex values and override them via the theme's module template override folder.
- Clear the PrestaShop cache: Admin → Advanced Parameters → Performance → 'Clear cache'. Verify with keyboard navigation.
<!-- ❌ WRONG: positive tabindex breaks natural tab order -->
<button tabindex="3">Submit</button>
<a href="/shop" tabindex="1">Shop Now</a>
<div tabindex="2">Custom widget</div>
<!-- ✅ CORRECT: native elements use no tabindex (natural order) -->
<button>Submit</button>
<a href="/shop">Shop Now</a>
<!-- ✅ CORRECT: non-native element made focusable in natural order -->
<div tabindex="0" role="button" aria-label="Custom widget">Custom widget</div>
<!-- ✅ CORRECT: element only focused by JavaScript (e.g., modal), not in tab flow -->
<div tabindex="-1" id="modal-container" role="dialog" aria-modal="true">...</div>Replace any positive tabindex value with 0 (or remove the attribute). Use -1 only for programmatic focus.
Valid langQuick win
Add or correct the `lang` attribute on every HTML element that contains content in a different language, using a valid BCP 47 language code.
On PrestaShop
- PrestaShop generates the `<html lang>` attribute from the active language. Go to Admin → International → Translations → Languages and verify the language code (ISO code field) for each installed language is a valid two-letter ISO 639-1 code (e.g. `en`, `fr`, `de`).
- In your active theme, open `templates/_partials/head.tpl` (or the root layout file). Find the `<html` tag and ensure it uses the Smarty variable `{$language.iso_code}` rather than a hard-coded value.
- If using a custom or third-party theme that hard-codes an invalid `lang` value, override the template in your child theme and correct it.
- Save, clear cache (Admin → Advanced Parameters → Performance → Clear cache), and verify.
<!-- Primary page language on the root element -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Store</title>
</head>
<body>
<p>Welcome to our store. Free shipping on orders over $50.</p>
<!-- Inline passage in a different language: add lang to the wrapping element -->
<p lang="fr">Bienvenue dans notre boutique. Livraison gratuite dès 50 €.</p>
<!-- Inline span for a short foreign-language phrase within an English sentence -->
<p>Our slogan is <span lang="de">Qualität zuerst</span>, which means "Quality first".</p>
</body>
</html>Set the primary language on the <html> tag and mark inline foreign-language passages with their own lang attribute.
Security (OWASP) · 24 fixes
Dmarc policy noneModerate effort
Strengthen your DMARC policy from p=none (monitor-only) to p=quarantine, then p=reject, so that emails fraudulently sent from your domain are blocked rather than silently allowed through.
On PrestaShop
- In PrestaShop Admin → Advanced Parameters → Email, set up an SMTP server with an authenticated relay (Mailgun, SendGrid, Brevo, etc.).
- Follow your relay provider's DKIM and SPF setup guide and publish the required DNS records at your DNS host.
- Edit the _dmarc TXT record at your DNS provider: change p=none to p=quarantine, monitor rua reports, then advance to p=reject.
v=DMARC1; p=quarantine; sp=quarantine; pct=100; rua=mailto:dmarc-reports@yourdomain.com; ruf=mailto:dmarc-failures@yourdomain.com; ri=86400; aspf=r; adkim=r; fo=1;This is the DNS TXT record value for _dmarc.yourdomain.com — paste this as the record content at your DNS provider. Replace the rua/ruf addresses with your own reporting inbox or DMARC reporting service address.
Hsts disabledQuick win
Set a valid HTTP Strict-Transport-Security (HSTS) header with a max-age of at least 31536000 (one year) and includeSubDomains to force browsers to always use HTTPS for your store.
On PrestaShop
- In PrestaShop Admin, go to Shop Parameters → General → Enable SSL on all pages → set to YES, and Enable SSL → YES.
- Then set the HSTS header at the server level: for Apache, edit .htaccess in your PrestaShop root and add inside <IfModule mod_headers.c>: Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
- For Nginx, add add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; to your server block and reload nginx.
- Verify: curl -I https://yourdomain.com | grep -i strict
Strict-Transport-Security: max-age=31536000; includeSubDomainsThis is an HTTP response header — it cannot be set in HTML meta tags. It must be set by your server, CDN, or platform. Shown here for reference only.
Hsts max age too shortQuick win
Increase your HSTS max-age directive to at least 31536000 seconds (1 year) so browsers enforce HTTPS connections for a meaningful period.
On PrestaShop
- PrestaShop itself does not manage HTTP security headers — they must be set at the web server level.
- For Apache hosting, add to your .htaccess file (in the PrestaShop root, after the RewriteEngine On line): <IfModule mod_headers.c> Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" </IfModule>
- For Nginx hosting, add inside your HTTPS server block: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
- Alternatively, use the PrestaShop module 'Security Headers' (available on PrestaShop Addons marketplace) which provides a UI to configure HSTS and other headers.
- Clear PrestaShop cache: Admin → Advanced Parameters → Performance → Clear cache. Verify with DevTools.
Strict-Transport-Security: max-age=31536000; includeSubDomainsThis is an HTTP response header — it must be sent by your server, NOT as an HTML meta tag. The meta tag is not supported for HSTS.
Https not availableQuick win
Enable HTTPS by installing a valid SSL/TLS certificate and redirecting all HTTP traffic to the secure HTTPS version of your store.
On PrestaShop
- Install an SSL certificate via your hosting control panel (cPanel Let's Encrypt or your host's SSL manager).
- In your PrestaShop Back Office, go to Shop Parameters → General.
- Set 'Enable SSL' to 'Yes' and 'Enable SSL on all pages' to 'Yes'.
- Save the configuration. PrestaShop will now serve all pages over HTTPS and redirect HTTP requests automatically.
- Flush PrestaShop's cache under Advanced Parameters → Performance → Clear cache.
Info disclosure serverQuick win
Remove or suppress the HTTP `Server` response header so your web server no longer advertises its software name and version number to the public internet.
On PrestaShop
- PrestaShop is self-hosted — apply the fix at the web server level, not within PrestaShop's admin.
- For Apache: add `ServerTokens Prod` and `ServerSignature Off` to `httpd.conf` or the `.htaccess` file in the PrestaShop root.
- For nginx: add `server_tokens off;` to the nginx server block for your PrestaShop site, then reload nginx.
- Verify with `curl -I https://yourstore.com` — `Server` should be absent or show only `Apache` or `nginx` with no version number.
<!-- The Server header cannot be controlled from HTML.
Apply the fix at your web server, CDN, or hosting control panel
using one of the platform-specific configurations shown below. -->There is no HTML tag to set or suppress the Server header — it is purely a server/CDN-level configuration. Use the server-specific snippets below.
Info disclosure x powered byQuick win
Remove or suppress the X-Powered-By HTTP response header so your server stops advertising which technology stack, hosting platform, or software version powers your store.
On PrestaShop
- PrestaShop runs on PHP/Apache or Nginx — suppress the header at the PHP and server layers.
- Add header_remove('X-Powered-By'); at the top of index.php in your PrestaShop root directory.
- For Apache: add 'Header unset X-Powered-By' to your .htaccess file.
- For Nginx: add 'fastcgi_hide_header X-Powered-By;' inside the location ~ \.php$ block of your server config.
- Also set expose_php = Off in your php.ini to prevent PHP version leakage via this header.
<!-- X-Powered-By is an HTTP response header set by the server, not in HTML markup.
Use the server-side code snippets below for your specific platform. -->There is no HTML tag for this — it is a server-side HTTP response header. Use the server/framework snippets below.
Insecure cookieModerate effort
Set the HttpOnly, Secure, and SameSite flags on all session and authentication cookies to prevent them from being stolen or misused by attackers.
On PrestaShop
- Open config/config.inc.php and locate or add session cookie configuration.
- In the PrestaShop back office: go to Advanced Parameters → Administration → and enable 'SSL' and 'SSL on all pages' (requires SSL certificate).
- Edit config/defines.inc.php or override the cookie class to set httponly and secure flags: in classes/Cookie.php (or an override), ensure __construct passes httponly=true and secure=true to setcookie().
- For a cleaner approach, create a file override/classes/Cookie.php and override the constructor to always set these flags.
- Clear the PrestaShop cache: Advanced Parameters → Performance → Clear cache.
- Verify with browser DevTools.
<?php
// File: override/classes/Cookie.php
class Cookie extends CookieCore
{
public function __construct($name, $path = '', $expire = null, $shared_urls = null, $standalone = false, $secure = true)
{
// Force secure and httponly for all cookies
parent::__construct($name, $path, $expire, $shared_urls, $standalone, true);
// Ensure the httponly flag is set
$this->_httponly = true;
}
}Create override/classes/Cookie.php to override the Cookie constructor without modifying core files.
Missing content security policyModerate effort
Add a Content-Security-Policy (CSP) HTTP response header to every page to block unauthorized scripts, styles, and resources from loading in your visitors' browsers.
On PrestaShop
- Add the CSP header in your .htaccess file (Apache): Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self';"
- For Nginx servers, add add_header Content-Security-Policy "..." always; in the server block.
- Alternatively, install a PrestaShop security module such as 'Security Headers' from the PrestaShop Marketplace.
- Start in Report-Only mode first to avoid breaking legitimate third-party scripts loaded by PrestaShop modules.
Missing dmarcQuick win
Add a DMARC DNS TXT record at _dmarc.yourdomain.com to tell receiving mail servers how to handle emails that fail authentication checks for your domain.
On PrestaShop
- PrestaShop does not control DNS. Access your hosting control panel (cPanel/Plesk) or external DNS provider.
- Add TXT record: Name = _dmarc, Value = v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com, TTL = 3600.
- In PrestaShop Back Office → Advanced Parameters → Email, configure SMTP to use an authenticated provider (SendGrid, Mailgun) that supplies DKIM keys, then add those DKIM DNS records before enforcing DMARC.
_dmarc.yourdomain.com. 3600 IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com; ruf=mailto:dmarc-forensic@yourdomain.com; sp=none; adkim=r; aspf=r"Add this as a DNS TXT record at your domain registrar or DNS provider. Replace yourdomain.com and the rua address with your actual domain.
Missing permissions policyQuick win
Add a Permissions-Policy HTTP response header to your store to explicitly restrict which browser features (camera, microphone, geolocation, etc.) pages and embedded third-party scripts are allowed to access.
On PrestaShop
- Apache: Edit your .htaccess or virtual host config in the PrestaShop root. Add the Header set directive inside <IfModule mod_headers.c> (see Apache snippet).
- Nginx: Add the add_header directive in the server {} block of your PrestaShop nginx config.
- Module approach: Search PrestaShop Addons marketplace for a 'security headers' module, install it, and configure the Permissions-Policy header value.
- Verify in DevTools → Network → Response Headers.
<!-- NOT recommended — use a real HTTP header instead. Shown for reference only. -->
<meta http-equiv="Permissions-Policy" content="camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()">Meta tag delivery is not recommended — use HTTP headers instead. Included for reference only; browser support is partial.
Missing referrer policyQuick win
Add a Referrer-Policy HTTP response header set to strict-origin-when-cross-origin to control what URL information browsers send when users navigate away from your store.
On PrestaShop
- Edit the .htaccess file in your PrestaShop root directory (for Apache servers) and add: Header always set Referrer-Policy "strict-origin-when-cross-origin"
- For Nginx, add add_header Referrer-Policy "strict-origin-when-cross-origin" always; inside your server{} block and reload Nginx.
- Alternatively, install a security-headers module from the PrestaShop Addons marketplace.
- Verify in DevTools → Network → Response Headers.
<meta name="referrer" content="strict-origin-when-cross-origin">Add inside the <head> element of every HTML page as a fallback when HTTP header control is unavailable.
Missing security txtQuick win
Create and publish a /.well-known/security.txt file with at least a Contact: line so security researchers know how to report vulnerabilities responsibly.
On PrestaShop
- Connect via SFTP to your PrestaShop root directory.
- Create a .well-known folder at the root if it does not exist, then create security.txt inside it.
- Verify Apache or Nginx does not block .well-known — add an allow rule if necessary.
- Visit https://yourdomain.com/.well-known/security.txt to confirm a 200 response.
Contact: mailto:security@yourdomain.com
Expires: 2026-12-31T23:59:00.000Z
Preferred-Languages: en
Policy: https://yourdomain.com/security-policy
Acknowledgments: https://yourdomain.com/security-acknowledgmentsSave this as security.txt and place it at /.well-known/security.txt on your web server. Update all placeholder values before publishing.
Missing spfQuick win
Add a DNS TXT record containing a valid SPF policy to your domain so that email receivers can verify which servers are authorised to send mail on your behalf.
On PrestaShop
- DNS is managed at your registrar or hosting control panel, not inside PrestaShop.
- Add a TXT record at '@' with your SPF value at your DNS provider.
- In PrestaShop back office, go to Advanced Parameters → Email to see the outbound mail method (PHP mail vs. SMTP) and the SMTP server configured. Add that provider's SPF include: directive.
- Verify propagation with a free SPF lookup tool.
v=spf1 include:_spf.google.com include:sendgrid.net include:spf.mailgun.org -allAdd this as a DNS TXT record at your domain apex ('@'). Replace the include: values with the exact hostnames from each of your email providers' SPF documentation. Only one SPF TXT record is allowed per domain — combine all providers into one record.
Missing strict transport securityQuick win
Add the Strict-Transport-Security (HSTS) response header to every HTTPS page so browsers always use an encrypted connection to your store.
On PrestaShop
- Log in to your hosting control panel or SSH into your server.
- For Apache hosting: Edit the .htaccess file in your PrestaShop root directory OR your Apache VirtualHost config for the HTTPS site. Add the HSTS Header directive from the Apache snippet below.
- For Nginx hosting: Edit your Nginx server block for port 443 and add the add_header directive from the Nginx snippet below, then reload Nginx.
- Alternatively, use a PrestaShop security module (e.g., 'HTTP Headers Manager' from the Addons Marketplace) that lets you set security headers from the PrestaShop back office.
- Go to Admin → Shop Parameters → General and ensure 'Force SSL for all pages' (SSL enabled on all pages) is set to Yes before deploying HSTS.
- Verify the header is present in browser DevTools after saving.
<!-- HSTS cannot be set via an HTML meta tag. It MUST be sent as an HTTP response header. Use the server-level examples below. -->
<!-- Correct header value -->
<!-- Strict-Transport-Security: max-age=31536000; includeSubDomains -->Meta tags cannot set HSTS — it must be a real HTTP response header. This is a reminder of the correct header value to use at the server level.
Missing x content type optionsQuick win
Add the `X-Content-Type-Options: nosniff` HTTP response header to every page and asset your store serves.
On PrestaShop
- For Apache hosting: add `Header always set X-Content-Type-Options "nosniff"` inside `<IfModule mod_headers.c>` in your `.htaccess` or VirtualHost config.
- For Nginx hosting: add `add_header X-Content-Type-Options 'nosniff' always;` inside the `server {}` block.
- Alternatively, create or edit an override in `override/controllers/front/FrontController.php` and add `header('X-Content-Type-Options: nosniff');` in the `init()` method — but the server-config approach is preferred as it covers all assets.
- Clear PrestaShop cache: Admin → Advanced Parameters → Performance → Clear cache.
X-Content-Type-Options: nosniffThis is an HTTP response header — it is sent by the server, not placed in HTML markup. The examples below show where and how to set it.
Missing x frame optionsQuick win
Add the X-Frame-Options HTTP response header set to DENY or SAMEORIGIN to prevent your store's pages from being embedded in iframes on other sites.
On PrestaShop
- In PrestaShop, the recommended approach is to add the header at the web server level or via an override.
- For Apache: edit your .htaccess file in the PrestaShop root and add inside <IfModule mod_headers.c>: Header always set X-Frame-Options 'SAMEORIGIN'.
- For Nginx: in your server block config, add: add_header X-Frame-Options 'SAMEORIGIN' always; then reload Nginx.
- Alternatively, install a PrestaShop security module from the marketplace (search 'security headers') that adds HTTP security headers.
- Verify by checking response headers in browser DevTools on your store's homepage and checkout page.
X-Frame-Options: SAMEORIGINThis is an HTTP response header — it must be sent by the server, not a meta tag. It should appear in the HTTP response headers for every page.
Ssl cert expiring soonQuick win
Renew your SSL/TLS certificate before it expires to keep your store secure, trusted, and visible in search results.
On PrestaShop
- SSL is managed by your web host or server, not PrestaShop itself.
- Renew the certificate through your hosting control panel (cPanel/Plesk Let's Encrypt section or paid CA wizard).
- After renewal, in PrestaShop Admin go to Shop Parameters → General and ensure 'Enable SSL' and 'Enable SSL on all pages' are both set to Yes.
- Clear PrestaShop's cache under Advanced Parameters → Performance → Clear cache after confirming the new certificate is active.
<!-- Place this in your maintenance/holding page only if HTTPS is temporarily unavailable -->
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">Meta-refresh redirect for maintenance: not applicable to SSL renewal. Instead, use this HTML to display a temporary notice if your cert has lapsed and you need to redirect HTTP traffic while fixing HTTPS.
Ssl cert invalidModerate effort
Install a valid SSL/TLS certificate that exactly matches your store's domain name, so browsers trust your site and customer data is encrypted in transit.
On PrestaShop
- SSL in PrestaShop is managed at the server level (same as self-hosted Magento — install the certificate via your hosting panel or server config).
- After installing the correct certificate on your server, go to PrestaShop Admin → Shop Parameters → General. Set 'Enable SSL' to Yes and 'Enable SSL on all pages' to Yes. Save.
- If you use a hosting control panel (cPanel), use Security → SSL/TLS → Let's Encrypt to provision a free certificate for your exact domain, then enable SSL in PrestaShop Admin as above.
Ssl errorModerate effort
Replace or reissue your SSL/TLS certificate so it is valid for the exact domain name your store uses, eliminating the hostname mismatch error.
On PrestaShop
- SSL is managed at the hosting layer for self-hosted PrestaShop installations.
- Log in to your hosting control panel and issue or re-issue an SSL certificate (Let's Encrypt recommended) covering yourstore.com and www.yourstore.com.
- In PrestaShop Admin → Shop Parameters → General, enable 'SSL' and 'Force SSL on all pages'.
- Ensure 'Shop domain' and 'SSL domain' fields (Admin → Configure → Shop Parameters → General → Set Shop URL) exactly match the domain on the certificate.
- Clear PrestaShop cache: Admin → Advanced Parameters → Performance → Clear cache.
Ssl not accessibleModerate effort
Enable HTTPS on your store by opening port 443 and installing a valid SSL/TLS certificate so every page is served over a secure connection.
On PrestaShop
- Ensure port 443 is open on your server/hosting firewall and a valid SSL certificate is installed (via cPanel SSL/TLS or Certbot).
- In PrestaShop Admin → Shop Parameters → General, enable 'SSL' by toggling 'Enable SSL' to 'Yes'.
- Also enable 'Enable SSL on all pages' to force HTTPS sitewide.
- Click 'Save'. PrestaShop will now serve all pages over HTTPS via port 443.
Vulnerable component axiosModerate effort
Update the Axios JavaScript library to its latest stable release to eliminate known security vulnerabilities in your storefront code.
On PrestaShop
- On your server, navigate to your PrestaShop root and search theme files (themes/your-theme/assets/js/) and modules for axios.
- If found in a theme JS file or CDN tag, update to the latest version URL or rebuild the bundle locally and re-upload via FTP.
- If a module loads the vulnerable Axios, check the PrestaShop Addons marketplace for an updated module version, or contact the module developer.
<!-- Load the latest Axios from jsDelivr CDN (always points to newest stable release) -->
<script src="https://cdn.jsdelivr.net/npm/axios@latest/dist/axios.min.js" integrity="" crossorigin="anonymous"></script>
<!-- OR pin to a specific safe version (recommended for production stability) -->
<script src="https://cdn.jsdelivr.net/npm/axios@1.7.9/dist/axios.min.js" crossorigin="anonymous"></script>Replace any hardcoded Axios CDN script tag in your HTML templates with this — always uses the latest stable version.
Vulnerable component jqueryModerate effort
Update jQuery to the latest stable version (3.7+) to eliminate known cross-site scripting (XSS) and prototype-pollution vulnerabilities that attackers actively exploit.
On PrestaShop
- In PrestaShop Admin, go to Modules → Module Manager and update all modules to their latest versions — modules frequently bundle their own jQuery.
- Go to Design → Theme & Logo → check for theme updates. In your theme directory ('themes/[your-theme]/'), search for 'jquery' in .tpl and .js files to find hard-coded inclusions.
- If jQuery is loaded in 'themes/[your-theme]/templates/_partials/head.tpl' or a layout file, update the version number in the CDN URL or replace the local file.
- PrestaShop core itself ships jQuery — updating PrestaShop to the latest minor/patch version via the 1-Click Upgrade module will update the core jQuery.
- After changes, clear the cache via Advanced Parameters → Performance → Clear cache, then verify with DevTools.
<!-- Remove your old jQuery script tag and replace with this -->
<script
src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
crossorigin="anonymous"></script>Replace any old jQuery <script> tag in your HTML <head> with this. Get the current SRI hash from https://code.jquery.com/
Weak spfQuick win
Add a hard-fail (-all) or soft-fail (~all) terminator to your SPF DNS record so that mail servers reject or flag email sent from servers not explicitly authorized by your domain.
On PrestaShop
- PrestaShop does not manage DNS. Log in to your domain registrar or hosting DNS panel.
- Find the TXT record for your root domain (@) starting with v=spf1.
- If you send email via your server's native PHP mail or a configured SMTP (set in PrestaShop Back Office → Advanced Parameters → Email), ensure the sending server IP or SMTP relay's include: entry is present.
- Append -all to the end of the record and save.
v=spf1 include:_spf.google.com include:sendgrid.net -allDNS TXT record value for your root domain (@). Replace the include/redirect mechanisms with the ones your email service provider specifies, and always end with -all or ~all.
X content type options weakQuick win
Set the X-Content-Type-Options response header to exactly `nosniff` (once, not duplicated) on every page and asset your store serves.
On PrestaShop
- Edit your Apache `.htaccess` file in the PrestaShop root, or your Nginx server block config.
- Apache: add `Header always set X-Content-Type-Options "nosniff"` inside a `<IfModule mod_headers.c>` block.
- Nginx: add `add_header X-Content-Type-Options "nosniff" always;` in the server block.
- Remove any duplicate directives or conflicting module settings.
- Reload the web server and verify with DevTools.
Site Lifecycle · 12 fixes
Cms versionModerate effort
Update your CMS and ecommerce platform to the latest stable version immediately to close known security vulnerabilities and maintain vendor support.
On PrestaShop
- Log in to the PrestaShop back office. Go to Advanced Parameters > Information to see the installed version.
- Check the latest release at github.com/PrestaShop/PrestaShop/releases or prestashop-project.org.
- Use the official '1-Click Upgrade' module (available in the PrestaShop Addons marketplace) for minor/patch upgrades: Modules > Module Manager, search '1-Click Upgrade', install, then follow the wizard.
- For major version upgrades (e.g., 1.7 to 8.x), back up the database and files first, then follow the manual upgrade guide in PrestaShop documentation.
- After upgrading, clear the cache: Advanced Parameters > Performance > Clear Cache, and verify front-office and checkout.
#!/bin/bash
# PrestaShop — pre-upgrade backup via SSH
# Run from your web root (the directory containing PrestaShop)
# 1. Check installed version
cat app/AppKernel.php | grep 'version'
# OR check the _PS_VERSION_ constant:
php -r "define('_PS_ROOT_DIR_', getcwd()); require('config/config.inc.php'); echo _PS_VERSION_ . PHP_EOL;"
# 2. Back up the database (replace values)
mysqldump -u DB_USER -pDB_PASS DB_NAME > prestashop_backup_$(date +%Y%m%d).sql
# 3. Back up files
tar -czf prestashop_files_$(date +%Y%m%d).tar.gz \
--exclude='./var/cache' \
--exclude='./var/logs' \
.
# After backup, use the 1-Click Upgrade module in the back office
# or follow the manual upgrade guide at devdocs.prestashop-project.orgRun this via SSH to back up before using the 1-Click Upgrade module, or for manual upgrade verification of the installed PrestaShop version.
Domain expiryQuick win
Log in to your domain registrar, confirm your domain's expiry date, and enable auto-renew so your store never goes offline due to an expired domain.
On PrestaShop
- PrestaShop does not manage domain registration. Log in to your hosting provider's control panel (cPanel, Plesk) or your registrar directly and enable auto-renew on the domain.
<!-- No HTML change required. Domain renewal is managed at your domain registrar's dashboard (e.g. GoDaddy, Namecheap, Cloudflare Registrar). Log in, find the domain, enable auto-renew, and confirm a valid payment method is on file. -->No code change is needed on your website — this fix is completed entirely in your registrar's dashboard. There is no markup or configuration file involved.
Gtm auditModerate effort
Install Google Tag Manager on your store and configure it to fire GA4 with full ecommerce event tracking (view_item, add_to_cart, purchase, and more).
On PrestaShop
- From the PrestaShop back office, go to Modules › Module Manager and search for 'Google Tag Manager' — install 'Google Tag Manager by Business Tech' or the official GTM module.
- Enter your GTM Container ID in the module configuration and enable enhanced ecommerce data layer output.
- The module will push ecommerce events (view_item, add_to_cart, purchase) to the data layer automatically.
- In GTM, create GA4 Event tags and publish. Verify with GTM Preview mode.
<!-- Google Tag Manager (paste in <head>) -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
<!-- End Google Tag Manager -->
<!-- Google Tag Manager (noscript) (paste immediately after <body>) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->Replace GTM-XXXXXXX with your real Container ID. First snippet goes in <head>, second immediately after <body>.
Html langQuick win
Add a lang attribute to your site's <html> tag that matches the primary language of your store's content.
On PrestaShop
- Via FTP or your hosting file manager, navigate to your active theme folder: themes/<your-theme>/templates/
- Open _partials/head.tpl or the root layout file (often layouts/layout-full-width.tpl or similar).
- Find the <html tag and add lang="{$language.iso_code|escape:'html':'UTF-8'}" to output the correct language code dynamically per PrestaShop's language object.
- Save and clear the PrestaShop cache via Admin → Advanced Parameters → Performance → Clear cache.
<!DOCTYPE html>
<html lang="{$language.iso_code|escape:'html':'UTF-8'}">
<head>
<meta charset="utf-8">
<title>{$page.meta.title|escape:'html':'UTF-8'}</title>
{block name='head_stylesheet'}{/block}
</head>
<body>
{block name='content'}{/block}
</body>
</html>In themes/<your-theme>/templates/_partials/head.tpl (or root layout .tpl) — outputs the ISO language code dynamically.
Lifecycle oos schema not updatedModerate effort
Update the `offers.availability` value in your Product structured data from `InStock` to `OutOfStock` (or `PreOrder`) whenever a product sells out, so Google's records always match your actual inventory.
On PrestaShop
- PrestaShop outputs Product schema through its theme layer. Open your active theme's `templates/catalog/product.tpl` file (via FTP or the theme editor if available).
- Search for `availability` or `InStock` in the template. The correct Smarty conditional to use is: `{if $product.availability eq 'available'}https://schema.org/InStock{else}https://schema.org/OutOfStock{/if}`.
- Alternatively, install a dedicated SEO module (e.g. SEO Expert, Yoast for PrestaShop) that handles schema dynamically and set availability to read from live stock.
- Clear PrestaShop's smarty cache under Advanced Parameters → Performance → Clear Cache, then validate with Google's Rich Results Test.
{* Inside catalog/product.tpl — find the application/ld+json block and replace the availability line *}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "{$product.name|escape:'html':'UTF-8'}",
"sku": "{$product.reference|escape:'html':'UTF-8'}",
"description": "{$product.description_short|strip_tags|escape:'html':'UTF-8'}",
"offers": {
"@type": "Offer",
"priceCurrency": "{$currency.iso_code}",
"price": "{$product.price_amount}",
"availability": "{if $product.availability eq 'available'}https://schema.org/InStock{else}https://schema.org/OutOfStock{/if}",
"itemCondition": "https://schema.org/NewCondition"
}
}
</script>Add or replace the availability line inside your active theme's catalog/product.tpl (Smarty template). This conditional reads the PrestaShop availability variable.
Lifecycle orphaned productsModerate effort
Add internal links pointing to every orphaned product page from relevant category pages, collection pages, or other on-site content so search crawlers and shoppers can discover them.
On PrestaShop
- Go to Catalog → Products and open each orphaned product.
- Click the 'Associations' tab and add the product to all relevant categories under 'Categories' and set an appropriate 'Default category.' Save.
- Go to Catalog → Categories to create any missing categories and then assign products.
- Add the category to the nav via Modules → Modules Manager → search 'Main Menu' → Configure, then add the category.
<!-- Add this inside a relevant category page, blog post, or 'Featured Products' section -->
<a href="/products/your-orphaned-product-slug">Product Display Name</a>Add a simple text link from any existing page (category, homepage, blog post) pointing directly to the orphaned product URL.
Lifecycle products missing from sitemapQuick win
Add all canonical product URLs to your XML sitemap so search engines discover and index every product page quickly.
On PrestaShop
- Go to Admin → Modules → search for 'Google Sitemap' → install or configure the native Google Sitemap module.
- In the module settings, ensure 'Products' is checked under 'Pages to include in sitemap'.
- Click 'Generate Sitemap' and note the output file path (typically yourdomain.com/sitemap.xml).
- Set up a cron job or use the module's scheduled regeneration to keep the sitemap current as products are added.
- Submit the sitemap in Google Search Console → Indexing → Sitemaps.
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<!-- Example product page entry -->
<url>
<loc>https://www.example.com/products/blue-running-shoes</loc>
<lastmod>2024-06-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<!-- Repeat one <url> block per canonical product page -->
<url>
<loc>https://www.example.com/products/red-yoga-mat</loc>
<lastmod>2024-05-20</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
</urlset>Standard XML sitemap structure. Place this at yourdomain.com/sitemap.xml (or reference it from a sitemap index file).
Lifecycle products too deepModerate effort
Move deeply buried product pages closer to the homepage by flattening your site's navigation structure so every product is reachable within 4 clicks (ideally 3) from the homepage.
On PrestaShop
- Go to Catalog → Categories in the PrestaShop back office.
- Identify categories that are children of children — these create deep click paths.
- Edit each overly nested category and change its parent to a higher-level category using the 'Parent category' dropdown, then save.
- Go to Modules → Module Manager and configure your menu module (e.g. Main Menu or ps_mainmenu) to include the restructured categories as direct top-level or first-level items.
- On your homepage (via Modules → Home Featured or similar), add the previously buried products to the featured products list to create a 1-click homepage path.
<!-- Featured Products section on homepage or top category page -->
<section aria-label="Featured Products">
<h2>Featured Products</h2>
<ul>
<li><a href="/products/product-name-1">Product Name 1</a></li>
<li><a href="/products/product-name-2">Product Name 2</a></li>
<li><a href="/products/product-name-3">Product Name 3</a></li>
</ul>
</section>Add direct internal links to deep products from your homepage or top-level category pages. Place this markup in a 'Featured Products' or 'Quick Links' section.
Mixed contentModerate effort
Ensure every resource (images, scripts, stylesheets, fonts, iframes) on your store is loaded over HTTPS, never HTTP, so browsers never block or warn about mixed content.
On PrestaShop
- Go to Admin → Shop Parameters → General. Enable 'Force SSL on all pages' to ensure all generated URLs are HTTPS.
- Go to Admin → Design → Theme & Logo → your theme files (or via FTP to /themes/yourtheme/) and search template .tpl files for 'http://' — update any hardcoded asset URLs.
- Check Admin → Design → Pages (CMS pages) for inline HTTP image/script references and update them.
- Clear the cache: Advanced Parameters → Performance → Clear cache.
- Re-test with browser DevTools Console.
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">Add this meta tag inside <head> to instruct browsers to automatically upgrade HTTP sub-requests to HTTPS. This is a safety net — fix the root URLs too.
Mobile viewportQuick win
Add a responsive viewport meta tag to every page so mobile browsers render your site at the correct width and scale.
On PrestaShop
- In your PrestaShop admin, go to Design → Theme & Logo → click 'Your current theme' to find the active theme name.
- Via FTP/SFTP, navigate to `themes/<your-theme>/templates/_partials/head.tpl`.
- Search for `meta name="viewport"`. If missing, add `<meta name="viewport" content="width=device-width, initial-scale=1">` inside the `<head>` block.
- Save, clear the PrestaShop cache under Advanced Parameters → Performance → Clear cache, and verify on mobile.
{* File: themes/<your-theme>/templates/_partials/head.tpl *}
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
{block name='head_seo'}{/block}
{block name='head_css'}{/block}
</head>Edit via FTP at themes/<your-theme>/templates/_partials/head.tpl, then clear cache in Admin → Performance.
Server versionQuick win
Remove or suppress HTTP response headers that advertise your server software name and version to the public internet.
On PrestaShop
- Add 'expose_php = Off' in 'php.ini' to remove 'X-Powered-By: PHP/x.x'.
- In Apache, add 'Header unset X-Powered-By', 'ServerTokens Prod', and 'ServerSignature Off' to your VirtualHost or '.htaccess'.
- In Nginx, add 'server_tokens off;' and 'fastcgi_hide_header X-Powered-By;' to your server block.
- Optionally install a PrestaShop security module (e.g. 'Security Lite') that can add/remove response headers from the admin back office.
- Verify via DevTools or 'curl -I' that the headers no longer reveal software versions.
<!-- HTTP response headers cannot be set via HTML meta tags.
Use the server-side or CDN configuration snippets below. -->Meta tags cannot suppress HTTP response headers — this must be done at the server/CDN layer. The snippets below cover the most common server/framework approaches.
Ssl expiryQuick win
Renew your TLS/SSL certificate before it expires and enable automatic renewal so your store never goes offline or shows security warnings.
On PrestaShop
- SSL is managed at the server/hosting level, not inside PrestaShop.
- Log in to cPanel or your hosting dashboard and enable AutoSSL or Let's Encrypt for your domain.
- Inside PrestaShop admin: go to Shop Parameters → General and enable 'SSL' and 'Enable SSL on all pages'.
- On self-hosted servers, use Certbot: `sudo certbot renew --dry-run` and verify the cron or systemd timer is active.
<!-- SERVER-SIDE redirect is strongly preferred. Example nginx config snippet below. -->
<!-- nginx: force HTTPS -->
<!-- server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
} -->Verify your HTTPS redirect is in place at the server/app level. This meta-refresh is a last-resort fallback only — implement a proper server-side 301 redirect instead.