Every Adobe Commerce (Magento) fix we catalog
SEOLZ catalogs 158 fixes for Adobe Commerce (Magento) across 5 areas — SEO, answer-engine readiness, accessibility, security and site-health. Each lists the exact steps for Adobe Commerce (Magento), with a link to the official docs.
SEO · 45 fixes
Catalog coverageModerate effort
Ensure every product and category page is crawlable and discoverable by submitting a complete XML sitemap, fixing internal links, and removing any crawl blocks so Google can index your full catalog.
On Adobe Commerce (Magento)
- Sitemap: Go to Marketing → SEO & Search → Site Map → Add Sitemap. Generate and submit the sitemap.xml URL to Google Search Console. Under Stores → Configuration → Catalog → XML Sitemap, ensure Products and Categories are included with frequency and priority set.
- robots.txt: Go to Content → Design → Configuration → [your store view] → Edit → scroll to 'Search Engine Robots'. Edit the robots.txt content to remove any Disallow rules blocking /catalog/product/ or /catalog/category/.
- Noindex: In Stores → Configuration → Catalog → Search Engine Optimization, check 'Use Canonical Link Meta Tag for Categories' and 'for Products' are enabled. Also check that Category and Product pages are not set to noindex via a third-party SEO extension.
- Internal linking: In Catalog → Categories, ensure every category is set to 'Include in Navigation Menu: Yes' and 'Is Active: Yes'. Assign products to categories via Catalog → Products → [product] → Categories tab.
- Layered navigation / faceted filtering: Magento's layered navigation can generate thousands of filtered URLs. Use Stores → Configuration → Catalog → Search Engine Optimization → 'Use Canonical Link Meta Tag for Categories' = Yes, and consider noindexing filter pages via a SEO extension to focus crawl budget on canonical pages.
Cwv clsModerate effort
Eliminate unexpected layout shifts by reserving explicit space for every image, video, embed, ad, and late-loading widget before it loads, so nothing on your page jumps around as it renders.
On Adobe Commerce (Magento)
- Images: In your theme's layout XML and template .phtml files, ensure all <img> tags include width and height attributes. For product images, edit app/design/frontend/<Vendor>/<Theme>/Magento_Catalog/templates/product/view/gallery.phtml and confirm width/height are output from the product image data object.
- Responsive images: Add CSS to your theme's web/css/source/ files: .product-image-container { aspect-ratio: 1/1; } to reserve square image space on product and category pages.
- Fonts: In web/css/source/_typography.less (or .scss), find @font-face declarations and add font-display: swap;. If using Google Fonts, add &display=swap to the URL in your layout XML <head> block.
- Banners and sliders (PageBuilder): If using Page Builder, open the Row or Banner element > Advanced and set a fixed min-height. Avoid 'auto-height' banner rows above the fold.
- Third-party modules: Audit app/design/frontend/<Vendor>/<Theme>/requirejs-config.js and layout XML for any module loading scripts in the <head>. Use the 'defer' or 'async' attribute for non-critical scripts. Move chat and review widget scripts to the footer by setting <move> in layout XML.
- Full Page Cache: In Stores > Configuration > Advanced > System > Full Page Cache, ensure Varnish or the built-in FPC is enabled. Cached pages paint faster and reduce the window in which late-loading elements can cause CLS.
Cwv lcpModerate effort
Reduce Largest Contentful Paint (LCP) to under 2.5 seconds by serving your hero image in a modern format, preloading it, and eliminating render-blocking resources.
On Adobe Commerce (Magento)
- Convert hero/banner images to WebP: in Admin → Content → Blocks or Content → Pages (for CMS banners), replace image src paths with WebP versions. Store your WebP files in pub/media/ alongside originals.
- Edit your theme's default_head_blocks.xml or the relevant CMS page XML layout file (app/design/frontend/Vendor/theme/Magento_Theme/layout/) to inject a preload link: <link rel='preload' src='{{media url='wysiwyg/hero.webp'}}' as='image' fetchpriority='high'/>.
- Enable Magento's built-in JS bundling and minification: Admin → Stores → Configuration → Advanced → Developer → JavaScript Settings → set 'Merge JS Files' and 'Minify JavaScript Files' to Yes. Enable CSS minification similarly under CSS Settings.
- Use a performance extension such as 'Hyvä Theme' or install 'MagePrince Lazy Load' configured to exclude the LCP/hero image from lazy loading.
- Enable full-page cache (Admin → System → Cache Management → Full Page Cache → Varnish or built-in) and configure a CDN via Admin → Stores → Configuration → General → Web → Base URLs to serve static assets from a CDN.
- Deploy static content (bin/magento setup:static-content:deploy) after theme file changes, then flush cache (bin/magento cache:flush) and verify LCP with PageSpeed Insights.
Duplicate meta descriptionModerate effort
Write a unique, page-specific meta description for every page on your store so Google can display a relevant snippet in search results.
On Adobe Commerce (Magento)
- For product pages: Admin → Catalog → Products → Edit product → 'Search Engine Optimization' section (left accordion) → 'Meta Description' field → Save.
- For category pages: Admin → Catalog → Categories → select category → 'Search Engine Optimization' section → 'Meta Description' → Save.
- For CMS pages: Admin → Content → Pages → Edit page → 'Search Engine Optimization' section → 'Meta Description' → Save.
- For the homepage: Admin → Content → Pages → find the 'Home Page' CMS page → Edit → SEO section → Meta Description → Save.
- For bulk product updates: use Admin → System → Import with the Products entity type; include the `meta_description` column in your CSV.
- Alternatively, use a third-party extension (e.g. Mageworx SEO Suite or Amasty SEO Toolkit) for bulk editing and template-based auto-generation of unique descriptions across large catalogs.
Duplicate titleModerate effort
Write a unique, descriptive title tag for every page on your store so no two pages share the same title.
On Adobe Commerce (Magento)
- For individual products: go to Catalog → Products, open the product, expand the 'Search Engine Optimization' section, and enter a unique value in the 'Meta Title' field.
- For categories: Catalog → Categories → select a category → expand 'Search Engine Optimization' → edit 'Meta Title'.
- For CMS pages: Content → Pages → open the page → expand 'Search Engine Optimization' → edit 'Meta Title'.
- For site-wide title suffix/prefix: Stores → Configuration → Catalog → Catalog → Product Fields Auto-generation — set the 'Meta Title' mask using variables like {{name}} to auto-populate unique titles from product names.
- For the store's title suffix: Stores → Configuration → General → Design → HTML Head → 'Default Title' and 'Title Suffix' — this appends your brand name automatically.
- For bulk updates across a large catalog, use a CSV import (System → Data Transfer → Import) with the 'meta_title' column populated per product, or use a third-party SEO extension (e.g. Mageworx SEO Suite).
Faceted url indexableModerate effort
Point every faceted/filter URL's canonical tag to the clean, unfiltered category URL (or add noindex) so Google treats filtered pages as one authoritative page instead of thousands of duplicates.
On Adobe Commerce (Magento)
- In the Admin panel, go to Stores → Configuration → Catalog → Catalog → Search Engine Optimization.
- Set 'Use Canonical Link Meta Tag for Categories' to 'Yes'. This makes all filtered/sorted category URLs emit a canonical pointing to the base category URL — this is the single most important setting.
- Also set 'Use Canonical Link Meta Tag for Products' to 'Yes' to handle product pages accessed via filtered navigation.
- To noindex layered navigation (filter) pages: go to Stores → Configuration → Catalog → Catalog → Layered Navigation and consider using a third-party extension like 'SEO Toolkit' (Mageworx) or 'Layered Navigation' (Amasty) which have dedicated noindex/canonical controls per filter attribute.
- In the same extension settings or via a custom module, configure which filter parameters should trigger noindex: common practice is to noindex all filtered URLs and rely on the canonical to pass authority to the base category.
- Flush the Magento cache after changes: go to System → Cache Management → Flush Magento Cache. Verify by viewing source of a filtered category URL and confirming `<link rel='canonical'>` points to the unfiltered category URL.
Images missing altModerate effort
Add descriptive alt text to every image on your store so search engines can understand them and all shoppers can access your content.
On Adobe Commerce (Magento)
- FOR PRODUCT IMAGES: In the Admin, go to Catalog > Products, open a product, scroll to the 'Images and Videos' section, hover over an image, and click the gear icon. Fill in the 'Alt Text' field and save the product.
- FOR CMS PAGE/BLOCK IMAGES: Go to Content > Pages (or Blocks), open the item, click the image in the WYSIWYG editor, then click the 'Insert Image' icon in the toolbar. In the dialog, complete the 'Image Description' (alt text) field.
- FOR BULK UPDATES: Use a product import CSV (System > Data Transfer > Import) — include the additional_images and additional_image_labels columns to set alt text for multiple products at once.
- FOR WIDGET/BANNER IMAGES: Go to Content > Elements > Widgets or Banners, open the relevant item, edit the image properties in the WYSIWYG, and update the alt text field.
Low performance scoreLarger project
Improve your Lighthouse/PageSpeed performance score by reducing page weight, deferring JavaScript, optimizing images, and fixing Core Web Vitals metrics so your store loads fast on mobile and desktop.
On Adobe Commerce (Magento)
- Full Page Cache — Stores → Configuration → Advanced → System → Full Page Cache: enable Varnish (strongly recommended for production) or the built-in FPC. This is the single highest-impact change for Magento performance.
- JS/CSS bundling and minification — Stores → Configuration → Advanced → Developer → JavaScript Settings: enable 'Minify JavaScript Files' and 'Move JS code to the bottom of the page'. Under CSS Settings, enable 'Minify CSS Files'.
- Image optimization — Use a Magento extension such as 'tinify/tinify' or a CDN with image optimization (Cloudflare Polish, Fastly Image Optimization, or Cloudinary). In your theme's layout XML, ensure product images use the correct image role and size declarations to prevent oversized images being served.
- Third-party scripts — Admin → Stores → Configuration → Sales → Google API / Marketing: disable integrations you are not actively using. Audit your theme's default.xml and head section for injected scripts.
- CDN for static assets — Stores → Configuration → General → Web → Base URLs (Secure): configure a CDN base URL so all static assets (JS, CSS, images) are served from CDN edge nodes.
- Deploy mode — Ensure your server is in 'production' mode (bin/magento deploy:mode:set production), which generates optimized, merged static files. Developer mode severely degrades performance.
- Hosting — Magento's minimum spec is high; upgrade to at least PHP 8.x with OPcache enabled, Redis for session and cache storage, and a server with ≥4 GB RAM if on VPS/dedicated.
Meta description too longQuick win
Shorten your meta description to 150–160 characters so Google displays your full message in search results instead of cutting it off with "…".
On Adobe Commerce (Magento)
- Log in to the Admin panel. For product pages: go to Catalog → Products → select the product → expand the 'Search Engine Optimization' section → edit the 'Meta Description' field. Keep it under 160 characters. Click 'Save'.
- For Category pages: go to Catalog → Categories → select the category → expand 'Search Engine Optimization' → edit 'Meta Description' → Save.
- For CMS pages: go to Content → Pages → select the page → expand 'Search Engine Optimization' → edit 'Meta Description' → Save.
- To set a store-wide default template: go to Stores → Configuration → Catalog → Catalog → 'Product Fields Auto-generation' — note that individual page overrides always take precedence.
- Consider installing an SEO extension (e.g. Mageworx SEO Suite) if you need bulk editing or automated length enforcement across thousands of products.
Meta description too shortModerate effort
Expand every meta description to 120–160 characters so Google shows your custom summary in search results instead of auto-generating one.
On Adobe Commerce (Magento)
- For a product: Admin → Catalog → Products → Edit the product → expand the 'Search Engine Optimization' section → edit the 'Meta Description' field → Save.
- For a category: Admin → Catalog → Categories → select the category → expand 'Search Engine Optimization' section → edit 'Meta Description' → Save.
- For a CMS page: Admin → Content → Pages → Edit the page → expand 'Search Engine Optimization' → edit 'Meta Description' → Save.
- For bulk product updates: use the Admin's mass-action product grid (Catalog → Products → select products → Actions → Update Attributes) — note that Meta Description is not a default bulk-edit attribute; use a Magento extension such as 'SEO Suite Ultimate' by Mirasvit or a direct database/import update via the Magento import/export CSV (System → Data Transfer → Import), ensuring the 'meta_description' column is populated.
Missing canonicalModerate effort
Add a self-referencing canonical tag to every page so Google knows which URL is the "official" version of that content.
On Adobe Commerce (Magento)
- Go to Stores → Configuration → Catalog → Catalog → Search Engine Optimization.
- Set 'Use Canonical Link Meta Tag for Categories' and 'Use Canonical Link Meta Tag for Products' both to 'Yes' — this enables automatic self-referencing canonicals.
- For CMS pages, go to Content → Pages → edit the relevant page → expand 'Search Engine Optimization' → enter the preferred URL in the 'URL Key' field; Adobe Commerce will generate the canonical from this.
- Clear the full-page cache after saving: System → Cache Management → Flush Magento Cache.
- For layered navigation (filtered category pages), consider installing the 'Magento 2 SEO' extension (e.g. by Mageworx or Amasty) to handle canonical tags on filtered/sorted URLs automatically.
Missing h1Quick win
Add a single, descriptive H1 heading to every page that currently lacks one, so search engines and shoppers immediately understand what the page is about.
On Adobe Commerce (Magento)
- **Product pages:** Admin → Catalog → Products → open the product → the 'Product Name' field is used as the H1 by default in Luma and Blank themes (rendered via 'catalog/product/view/title.phtml'). Confirm the template wraps it in `<h1 class="page-title">`.
- **Category pages:** Admin → Catalog → Categories → open the category → 'Name' field is the H1. Check theme file 'catalog/category/view/title.phtml'.
- **CMS Pages:** Admin → Content → Pages → open the page → the 'Content Heading' field explicitly renders as the `<h1>` on the page. If this field is blank, add your heading text here.
- **Theme override:** If a custom theme suppresses the H1, copy the relevant '.phtml' title template to your custom theme under 'app/design/frontend/[Vendor]/[Theme]/' and restore the `<h1>` tag.
- Flush the Magento cache after changes: Admin → System → Cache Management → Flush Magento Cache. Then verify with browser Dev Tools.
Missing meta descriptionQuick win
Write a unique meta description of 120–160 characters for every page so Google has compelling snippet text to show in search results.
On Adobe Commerce (Magento)
- For a product: Admin → Catalog → Products → click the product → expand 'Search Engine Optimization' section → fill 'Meta Description' field → Save.
- For a category: Admin → Catalog → Categories → select the category → expand 'Search Engine Optimization' section → fill 'Meta Description' → Save.
- For a CMS page: Admin → Content → Pages → select the page → expand 'Search Engine Optimization' section → fill 'Meta Description' → Save.
- For the homepage (which is typically a CMS page): Admin → Content → Pages → find the page set as homepage → same SEO section → Save.
- To set a store-wide default meta description template: Admin → Stores → Configuration → Catalog → Catalog → 'Product Reviews' / search for 'Meta Description' under the Search Engine Optimization sub-section → set a default template → Save Config.
Missing og descriptionQuick win
Add an og:description meta tag to every page so social platforms display a compelling preview when your store's links are shared.
On Adobe Commerce (Magento)
- Log in to the Admin Panel → go to Catalog → Products → select the product to edit.
- In the product edit screen, click the 'Search Engine Optimization' accordion section → fill in the 'Meta Description' field.
- To output this as og:description, open your theme's default_head_blocks.xml or the relevant layout XML (e.g. catalog_product_view.xml) and confirm or add: <meta property="og:description" content="{{escapeHtmlAttr $description}}" /> via a custom block or by editing app/design/frontend/<Vendor>/<theme>/Magento_Catalog/layout/catalog_product_view.xml.
- Alternatively, install a dedicated Open Graph extension from the Adobe Commerce Marketplace (search 'Open Graph') to handle og tags automatically from meta description fields.
- For categories: Catalog → Categories → select category → expand 'Search Engine Optimization' → fill in Meta Description.
- Clear the full-page cache (System → Cache Management → Flush Magento Cache), then validate with Facebook Sharing Debugger.
Missing og imageModerate effort
Add an og:image meta tag to every page so social media platforms and messaging apps display a rich preview image when someone shares your store's link.
On Adobe Commerce (Magento)
- In the Admin panel, go to Stores → Configuration → General → Design → HTML Head.
- Adobe Commerce does not output og:image tags by default in older versions. Install a dedicated extension such as 'MagePal Open Graph' (free) or a comprehensive SEO extension like 'Mageplaza SEO' or 'Amasty SEO Toolkit' to add og:image support.
- Once the extension is installed, configure it under Stores → Configuration → [Extension Name] → Social / Open Graph section. Set the product image as the og:image source for product pages (usually mapped to the product's base image) and configure fallback images for category and CMS pages.
- For custom theme implementations, edit your theme's 'default_head_blocks.xml' or the relevant layout XML to add the og:image meta tag, and use a block/helper to pull the product or category image URL dynamically.
- Ensure product images are properly uploaded and assigned as the product's 'Base Image' role in Catalog → Products → [Product] → Images and Videos.
- Clear the Magento cache (System → Cache Management → Flush Magento Cache) and validate with the Meta Sharing Debugger.
Missing og titleQuick win
Add an og:title meta tag to every page so your store looks great when shared on Facebook, Pinterest, LinkedIn, and other social platforms.
On Adobe Commerce (Magento)
- From the Admin panel, go to Content → Configuration → select your store view → Edit.
- Under 'HTML Head', confirm the Default Page Title is set — this feeds the base title but does NOT automatically create og:title.
- To add og:title sitewide, the recommended approach is to use an extension such as 'MagePlaza SEO' or 'Amasty SEO Toolkit' (available on the Adobe Commerce Marketplace) which will automatically generate og:title from product/category/page titles for all page types.
- If adding manually via theme: edit your theme's layout XML file (e.g. app/design/frontend/Vendor/theme/Magento_Theme/layout/default_head_blocks.xml) or override the head.phtml template to inject: <meta property="og:title" content="<?= $block->getTitle() ?>" />
- For product pages specifically, override Magento_Catalog/templates/product/view.phtml or use a layout XML update targeting catalog_product_view to inject the og:title with the product name variable.
- Clear the Magento cache (System → Cache Management → Flush Cache Storage) after changes, then validate with the Facebook Sharing Debugger.
Missing titleQuick win
Add a unique, descriptive title tag (30–60 characters) to every page that is missing one.
On Adobe Commerce (Magento)
- For a product: go to Catalog → Products → select the product → expand the 'Search Engine Optimization' section → fill in the 'Meta Title' field.
- For a category: go to Catalog → Categories → select the category → expand 'Search Engine Optimization' → fill in 'Meta Title'.
- For a CMS page: go to Content → Pages → select the page → expand 'Search Engine Optimization' → fill in 'Meta Title'.
- To set global title templates (applied automatically when no manual title is set): go to Stores → Configuration → Catalog → Catalog → search 'Title' and configure 'Product Title Suffix' or use the 'Meta Title' defaults.
- Click Save and flush the Magento cache: System → Cache Management → Flush Magento Cache.
Multiple h1Moderate effort
Reduce every page to exactly one H1 tag that clearly describes the page's main topic, removing or converting all extra H1s to lower-level headings (H2, H3, etc.).
On Adobe Commerce (Magento)
- In the Admin panel, go to Content → Design → Configuration → select your store view → HTML Head. Confirm the 'Page Title' render tag is set correctly (this controls the <title>, not the H1, but rules out confusion).
- The H1 on category pages comes from app/design/frontend/<Vendor>/<Theme>/Magento_Catalog/templates/category/view.phtml — SSH/FTP into this file and confirm only the category name is in <h1>.
- For product pages, check app/design/frontend/<Vendor>/<Theme>/Magento_Catalog/templates/product/view/title.phtml — the product name (<?= $block->getProduct()->getName() ?>) should be the sole H1.
- For CMS pages, go to Content → Pages → edit the page in the Page Builder. Inspect each Row/Column/Heading element and change any Heading widgets set to 'h1' to 'h2' or lower.
- Check your theme's header.phtml or header.html for any site-name or tagline wrapped in <h1> and change to <p> or <span>.
- After deploying changes (run 'bin/magento cache:clean'), verify on the live page using browser DevTools or a heading-audit tool.
Noindex detectedQuick win
Remove or replace the `noindex` directive on any page you want Google to find and rank, then verify the change with Google Search Console.
On Adobe Commerce (Magento)
- For site-wide robots settings: Admin → Content → Design → Configuration → select the store view → expand 'Search Engine Robots' → set 'Default Robots' to `INDEX, FOLLOW`.
- For individual CMS pages: Admin → Content → Pages → edit the page → scroll to 'Search Engine Optimisation' section → set 'Robots' to `INDEX, FOLLOW`.
- For product pages: Admin → Catalog → Products → edit the product → Search Engine Optimization tab → set 'Robots' to `INDEX, FOLLOW`.
- For category pages: Admin → Catalog → Categories → edit the category → Search Engine Optimization section → set 'Robots' to `INDEX, FOLLOW`.
- Also check your theme's `default_head_blocks.xml` or `head.phtml` for any hard-coded noindex meta tags and remove them.
- Flush the Magento cache after changes: Admin → System → Cache Management → Flush Magento Cache.
Non self canonicalModerate effort
Ensure every page's canonical tag points to that same page's own URL — fix any canonical that currently points to a different page unless the redirect is genuinely intentional.
On Adobe Commerce (Magento)
- Log in to the Magento Admin Panel and navigate to Stores → Configuration → Catalog → Catalog → Search Engine Optimization.
- Review settings for 'Use Canonical Link Meta Tag for Categories' and 'Use Canonical Link Meta Tag for Products' — these are typically set to 'Yes' for self-referencing canonicals.
- For individual products/categories, go to Catalog → Products (or Categories) → open the item → Search Engine Optimization tab → check the 'URL Key' and any custom canonical field.
- If a third-party SEO extension (e.g., Mageplaza SEO, Amasty SEO) is installed, navigate to that extension's configuration in Stores → Configuration to find canonical override settings.
- Check your custom theme layout XML files (e.g., catalog_product_view.xml) for any block or template that outputs a custom canonical tag, and correct it.
- Flush cache: System → Cache Management → Flush Magento Cache, then verify via View Source.
Page speed warningModerate effort
Reduce your page load time to under 1.5 seconds by compressing images, eliminating render-blocking resources, and enabling caching so Google and shoppers experience a fast site.
On Adobe Commerce (Magento)
- Built-in caching: In Admin → System → Cache Management, enable all cache types and click 'Flush Magento Cache'. Enable Varnish full-page cache (FPC) if your hosting supports it (System → Configuration → Advanced → System → Full Page Cache → Caching Application → Varnish).
- JS/CSS minification: In Admin → Stores → Configuration → Advanced → Developer → JavaScript Settings, set 'Minify JavaScript Files' to Yes; under CSS Settings set 'Minify CSS Files' to Yes. Deploy static content after changes via CLI: 'php bin/magento setup:static-content:deploy'.
- Images: Use a Magento image optimisation extension (e.g. TinyPNG for Magento, or Amasty Image Optimizer) from Adobe Marketplace. In Stores → Configuration → Catalog → Storefront, enable 'Lazy Loading for Images'.
- CDN: In Stores → Configuration → General → Web → Base URLs (Secure), set your CDN base URL for static and media files. Fastly is included with Adobe Commerce Cloud and is configured in the Fastly extension under Stores → Configuration → Advanced → System.
- Third-party extensions: In Admin → System → Extensions, audit installed extensions. Disable or uninstall any extension not actively in use — each can add frontend JS/CSS.
- Verify: Run Google PageSpeed Insights and check Admin → Reports → Performance (New Relic if on Commerce Cloud) to identify remaining server-side bottlenecks.
Search results indexableModerate effort
Add a noindex robots meta tag to all internal search results pages and block the search path in robots.txt to prevent thin, duplicate, near-infinite pages from polluting Google's index.
On Adobe Commerce (Magento)
- Log in to your Magento Admin and go to Content → Design → Configuration → (your store view) → HTML Head.
- Magento does not have a built-in per-page noindex toggle for search. Instead, create a custom layout XML file: app/design/frontend/<Vendor>/<Theme>/Magento_CatalogSearch/layout/catalogsearch_result_index.xml
- Inside that file add: <page><head><meta name="robots" content="NOINDEX,FOLLOW"/></head></page> — Magento's layout system will inject this only on search result pages.
- For robots.txt: go to Content → Design → Search Engine Robots, choose your store view, and add to the 'Edit Custom Instruction of robots.txt File' field: Disallow: /catalogsearch/ (Magento's default search path).
- Flush the Magento cache under System → Cache Management, then verify by loading /catalogsearch/result/?q=test and checking View Source.
Seo category description missingQuick win
Write a unique 100–200 word description for every product category page explaining what it contains and who it's for.
On Adobe Commerce (Magento)
- In your Adobe Commerce admin, go to Catalog → Categories.
- Select the category from the left-hand tree.
- In the 'Content' section, find the 'Description' field (a WYSIWYG editor). Enter your 100–200 word description here.
- Scroll to the 'Search Engine Optimization' section on the same page to fill in the 'Meta Description' field separately.
- If the description doesn't display on the frontend, check your theme's category.phtml template to ensure it renders the {{block class="Magento\Catalog\Block\Category\View"}} description output.
- Click 'Save' and clear your full-page cache (System → Cache Management → Flush Magento Cache).
Seo category description thinModerate effort
Expand your category page description to at least 150–300 words of genuinely useful, keyword-rich content that explains what shoppers will find in the category.
On Adobe Commerce (Magento)
- Go to Admin → Catalog → Categories.
- Select the category from the left-hand category tree.
- In the 'Content' section (expand it), find the 'Description' WYSIWYG editor field.
- Write or paste your expanded category description.
- Optionally use the 'Add CMS Block' option to pull in reusable content blocks if descriptions are shared across multiple categories.
- Click 'Save' and then flush the Magento cache: System → Cache Management → Flush Magento Cache.
Seo category emptyQuick win
Either populate the empty category with relevant products, or apply a noindex tag and remove it from navigation so Google does not waste crawl budget on a page with no content.
On Adobe Commerce (Magento)
- To ADD products: Admin → Catalog → Categories → select the empty category → Products in Category tab → search and add products → Save.
- To NOINDEX: Admin → Catalog → Categories → select the empty category → Search Engine Optimization section → set 'robots' meta tag to 'NOINDEX, FOLLOW' (if a custom attribute exists), OR use a third-party SEO extension such as Mageplaza SEO or Amasty SEO Toolkit to set noindex per category without custom development.
- To HIDE from nav: Admin → Catalog → Categories → select the category → set 'Include in Navigation Menu' to 'No' → Save.
- To DELETE and REDIRECT: Admin → Catalog → Categories → select → Delete. Then Admin → Marketing → SEO & Search → URL Rewrites → Add URL Rewrite → set old Request Path to the parent category or homepage Target Path with redirect type '301'.
Seo category sparseModerate effort
Add more products to the category or consolidate it with a closely related one so Google sees a substantive, valuable page worth ranking.
On Adobe Commerce (Magento)
- Log into the Admin Panel and go to Catalog → Categories. Locate the sparse category in the category tree.
- To add products: select the sparse category, click the 'Products in Category' tab, and use the grid to assign additional products; save the category.
- To add a description: in the sparse category's 'Content' tab, populate the 'Description' field with a unique paragraph and an engaging H1 (set in the 'Category Name' field or via a CMS block).
- To merge: move all products to the target category, then disable the old category (set 'Is Active' to No) and create a URL rewrite: go to Marketing → SEO & Search → URL Rewrites → Add URL Rewrite, set type to 'Custom', enter the old Request Path and the new Target Path, and set Redirect Type to '301 Permanent'.
- After confirming the redirect, delete or permanently disable the old category.
- Submit the destination URL in Google Search Console URL Inspection and request indexing.
Seo facet url not canonicalizedModerate effort
Add a canonical tag pointing filtered and sorted category URLs back to the clean (unfiltered) category page URL to prevent duplicate content and crawl waste.
On Adobe Commerce (Magento)
- In the Admin, go to Stores → Configuration → Catalog → Catalog → Search Engine Optimization.
- Set 'Use Canonical Link Meta Tag for Categories' to **Yes** — this instructs Magento to output a canonical on category pages pointing to the clean category URL, overriding layered navigation filter parameters.
- Also set 'Use Canonical Link Meta Tag for Products' to **Yes** for product pages.
- Clear the Magento cache: System → Cache Management → Flush Magento Cache.
- If you use third-party layered navigation extensions (e.g., Amasty Layered Navigation, Mirasvit Layered Navigation), check the extension's configuration for its own canonical URL settings — these extensions often have independent canonical controls that must also be enabled.
- Verify in a browser by navigating to a filtered category URL (with layered nav parameters), viewing page source, and confirming `<link rel='canonical'>` points to the base category URL without filter parameters.
Seo product description minimalModerate effort
Expand thin product descriptions to at least 300 words by adding specifications, use cases, and benefits so Google has enough content to rank and shoppers have enough information to buy.
On Adobe Commerce (Magento)
- Go to Admin Panel → Catalog → Products and click 'Edit' on the target product.
- Scroll to the 'Content' section and expand it to reveal the 'Description' (full) and 'Short Description' WYSIWYG fields.
- Click 'Edit with Page Builder' (or the WYSIWYG toggle) for the Description field. Add rows/columns with Text blocks, HTML blocks for spec tables, and Tab blocks for FAQ content.
- Ensure the Short Description (shown near the Add to Cart button) is also filled in with 2–3 benefit-focused sentences.
- For bulk updates, go to Catalog → Products → select multiple products → Actions → 'Update Attributes', or use a data import via System → Import with entity type 'Products' and update the description column in your CSV.
- Click 'Save' when done, then flush the Magento cache under System → Cache Management.
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 Adobe Commerce (Magento)
- Log into your Adobe Commerce Admin Panel and navigate to Catalog → Products.
- Click Edit on the product missing a description.
- Scroll to the Content section and expand it. You will see two fields: 'Description' (full, shown on product page) and 'Short Description' (shown in listings).
- Fill in the Description field with your 200–400 word copy using the WYSIWYG editor. Also fill the Short Description with a 1–2 sentence summary.
- Click Save. Adobe Commerce includes the product description in its built-in schema.org Product markup; confirm with Google's Rich Results Test.
- If using a third-party SEO extension (e.g., Mageworx SEO Suite), verify the extension maps the description field to the schema 'description' property correctly.
Seo product description thinModerate effort
Expand thin product descriptions to at least 300 words of unique, keyword-rich content that answers real shopper questions and gives Google enough text to understand what you sell.
On Adobe Commerce (Magento)
- Go to Admin Panel → Catalog → Products → click 'Edit' on the product.
- Expand the 'Content' section. You will see two fields: 'Short Description' (shown near the product image/price) and 'Description' (the full page description).
- Expand your full content in the 'Description' field using the WYSIWYG editor or HTML view. Target 300+ words here.
- Use the 'Short Description' field for a concise 1–3 sentence summary only.
- Click 'Save'. Adobe Commerce/Magento renders both fields as server-side HTML by default — fully crawlable.
- For bulk updates across large catalogs, use Admin → System → Import (CSV) or a third-party extension such as 'Improved Import & Export' to update descriptions programmatically.
Seo product image alt filenameModerate effort
Replace filename-based alt text on product images with short, descriptive phrases that accurately describe each image's content.
On Adobe Commerce (Magento)
- Go to Catalog → Products in the Admin Panel and open a product.
- Click the 'Images and Videos' tab.
- Click the image thumbnail you want to edit — a detail panel will expand.
- Find the 'Alt Text' field and enter your descriptive text.
- Click 'Save' on the product.
- For bulk updates, use a custom import script via the Magento REST API (PUT /V1/products/{sku}/media/{entryId}, updating the 'label' field, which maps to alt text), or use an extension such as 'Mass Product Actions' from the Magento Marketplace.
Seo product missing brandQuick win
Add a `brand` property to your Product structured data (schema.org/Product) so search engines can match your listings to brand-specific queries.
On Adobe Commerce (Magento)
- In the Magento admin, go to Stores → Attributes → Product and ensure a 'Manufacturer' or 'Brand' product attribute exists (create one if not, with scope: Global, input type: Text/Dropdown).
- Assign brand values to your products: Catalog → Products → edit a product → find the Brand/Manufacturer attribute and enter the brand name.
- Edit your theme's `Magento_Catalog/templates/product/view/` directory — specifically the JSON-LD or structured data template — to include the brand property. Add: `'brand' => ['@type' => 'Brand', 'name' => $product->getAttributeText('manufacturer')]` in the schema array.
- Alternatively, install a structured data extension (e.g. 'MageWorx SEO Suite' or 'Amasty SEO Toolkit') that automatically maps the brand/manufacturer attribute into Product schema.
- Clear the Magento cache (System → Cache Management → Flush Cache Storage) and validate a product page in Google's Rich Results Test.
Seo product missing gtinModerate effort
Add a valid GTIN (and/or MPN) to your Product structured data so Google can match your listings to its product catalog and show them in Shopping results and AI-powered product features.
On Adobe Commerce (Magento)
- In the Admin panel, go to Catalog → Products → select a product → expand the 'Search Engine Optimization' or 'Product Details' section.
- Adobe Commerce does not include a native GTIN field out of the box. Create a custom product attribute: Stores → Attributes → Product → Add New Attribute. Set attribute code to 'gtin', input type Text Field, and assign it to the relevant attribute set.
- Navigate back to Catalog → Products, open each product, and enter the GTIN value in the new 'gtin' attribute field.
- To inject this into the Product JSON-LD structured data, edit your theme's product schema template file — typically located at 'app/design/frontend/<Vendor>/<theme>/Magento_Catalog/templates/product/view/structured-data.phtml' (or override the core file at 'vendor/magento/module-catalog/view/frontend/templates/product/view/structured-data.phtml').
- Add the GTIN property to the JSON-LD output: '"gtin": "<?= $escaper->escapeHtml($product->getData('gtin')) ?>"'.
- Alternatively, install a structured data extension from the Magento Marketplace (search 'Product Schema GTIN') to handle this without core file modifications.
- Flush the Magento cache (System → Cache Management → Flush Cache) and verify with Google's Rich Results Test.
Seo product missing stock statusModerate effort
Add visible stock availability text to each product page and set the correct `availability` property (InStock, OutOfStock, or PreOrder) in your Product structured data (JSON-LD schema).
On Adobe Commerce (Magento)
- Adobe Commerce generates Product schema automatically. To confirm `availability` is included, go to Stores → Configuration → Catalog → Catalog → Search Engine Optimization and ensure structured data options are enabled.
- In the Admin, go to Catalog → Products → edit a product → Inventory tab and set 'Stock Availability' to 'In Stock' or 'Out of Stock'. This value feeds the auto-generated schema.
- If you need to customise the schema output, override the layout XML or the `Magento_Catalog` module's `product/view/schema.phtml` template in your custom theme under `app/design/frontend/<Vendor>/<Theme>/Magento_Catalog/templates/`.
- For the visible label, the default Luma/Blank theme already displays stock status near the Add-to-Cart button via the `stockStatus` template block — verify it is not hidden by your theme's CSS.
- For headless/PWA Studio storefronts, ensure the `availability` field is included in your Product GraphQL query and rendered in the product detail component.
- Use the Rich Results Test on a product URL to verify the `availability` field is correct after any changes.
Seo product no imagesModerate effort
Add at least one high-quality image to every product page so Google can index it and shoppers can see what they're buying.
On Adobe Commerce (Magento)
- In the Admin panel, go to Catalog → Products.
- Click 'Edit' on the product that is missing images.
- Scroll down to the 'Images and Videos' section and click the camera/upload icon to upload new image files.
- After uploading, click the image thumbnail to open its settings: assign roles (Base, Small, Thumbnail, Swatch) and enter alt text in the 'Alt Text' field.
- Ensure at least the 'Base Image' role is assigned so the image appears on the product detail page and in structured data.
- Click 'Save'. Run a full reindex (System → Index Management → Reindex) and flush the cache (System → Cache Management) to propagate the changes.
Seo product no spec tableModerate effort
Add a structured specifications table or list to every product page so search engines and AI tools can extract and surface factual product details, and shoppers can make faster buying decisions.
On Adobe Commerce (Magento)
- Go to Stores → Attributes → Product. Create a new attribute for each spec (e.g., 'material', 'net_weight_kg'). Set 'Used in product listing' and 'Visible on Catalog Pages on Storefront' to Yes.
- Assign each new attribute to the relevant Attribute Set (Stores → Attributes → Attribute Sets). This controls which product types show which specs.
- In the product edit screen (Catalog → Products → select product), fill in values for the new attributes under the relevant attribute group tab.
- The Luma/Blank theme automatically renders attributes assigned to the 'More Information' tab in the product page's tab block via the Magento_Catalog::product/view/attributes.phtml template. Customize the template in your custom theme at app/design/frontend/<Vendor>/<Theme>/Magento_Catalog/templates/product/view/attributes.phtml.
- For bulk population, use the Magento product CSV import (System → Data Transfer → Import, entity type 'Products') with columns matching attribute codes.
- Use a PIM tool like Akeneo with the Magento connector for enterprise catalog management.
Seo product price not in htmlModerate effort
Render product prices in server-side HTML so search engines and rich-result parsers can read them without executing JavaScript.
On Adobe Commerce (Magento)
- In the Magento admin, go to Stores → Configuration → Catalog → Price and check whether 'Catalog Price Scope' and any third-party dynamic pricing extension is configured to output prices via AJAX/JS rather than PHP templates.
- Locate the product price template file at vendor/magento/module-catalog/view/frontend/templates/product/price/ (or your theme override at app/design/frontend/<Vendor>/<Theme>/Magento_Catalog/templates/product/price/). Ensure prices are rendered via PHP template, not replaced by a JS component.
- If you use Magento's built-in 'price box' UI component, note that by default Magento 2 renders the base price in server-side HTML but may use JS for tier/group pricing. Ensure at least the base price is in the initial HTML.
- Disable or reconfigure any dynamic pricing or currency-conversion extensions that inject prices purely via JavaScript AJAX calls; configure them to use server-rendered fallback prices.
- Add or verify Product structured data: go to your theme's layout XML (catalog_product_view.xml) and confirm a JSON-LD block with 'offers.price' is included, or install an SEO extension such as MageWorx SEO Suite or Amasty SEO Toolkit.
- Flush cache (System → Cache Management → Flush Magento Cache), then use View Page Source on a product URL to confirm the price appears in the raw HTML response.
Seo product single imageModerate effort
Add at least 4 high-quality product images (multiple angles, detail shots, and lifestyle/in-use photos) to every product listing to increase click-through rates and conversions.
On Adobe Commerce (Magento)
- Go to Admin → Catalog → Products → select the product.
- Click the 'Images and Videos' section to expand it.
- Click the camera/upload area or drag image files in to add multiple images.
- For each image, click the three-dot menu (⋮) → 'Edit' to add Alt Text, set roles (Base, Small, Thumbnail, Swatch), and adjust sort order.
- Check that each image has at least one role assigned. Click 'Save' when finished.
Seo product title genericQuick win
Rewrite generic product page titles to include the product name plus at least one differentiating attribute (material, colour, size, pack size, or brand) in a natural, keyword-rich format.
On Adobe Commerce (Magento)
- Log in to the Admin Panel → Catalog → Products → [select your product] → Edit.
- Expand the 'Search Engine Optimization' section in the product edit page.
- Update the 'Meta Title' field with your optimised product title (this sets the <title> tag).
- Click 'Save'. Clear the full-page cache: System → Cache Management → Flush Magento Cache.
- For store-wide title templates, go to Stores → Configuration → Catalog → Catalog → Product Fields Auto-Generation to set a naming pattern, or use a third-party SEO extension (e.g. Mageplaza SEO) for advanced title templates.
Seo variant urls in sitemapModerate effort
Remove product variant URLs (e.g. ?variant=, ?sku=) from your XML sitemap so only the canonical product page URL is submitted to Google.
On Adobe Commerce (Magento)
- Go to Admin → Marketing → SEO & Search → Site Map. Review existing sitemap configurations and note which store views are affected.
- Go to Stores → Configuration → Catalog → XML Sitemap. Under 'Product Options', confirm 'Include in Sitemap' is set correctly. Adobe Commerce does not natively add ?___store= or configurable product option query parameters to the sitemap — check if a third-party SEO extension is the source.
- If using a sitemap extension (e.g., Amasty SEO Toolkit, Mirasvit SEO Suite), go to that extension's configuration in the Admin and look for a 'Product Variations', 'Configurable Product Options', or 'URL Parameters' setting and disable inclusion of variant URLs.
- Ensure all configurable product child (simple) product URLs either redirect to the parent configurable product URL or carry a canonical tag pointing to the parent. In Adobe Commerce, go to Stores → Configuration → Catalog → Search Engine Optimization → set 'Use Canonical Link Meta Tag for Products' to Yes.
- After saving configuration, regenerate the sitemap: Marketing → SEO & Search → Site Map → select your sitemap → Generate. Then resubmit the updated sitemap URL in Google Search Console.
Seo variant urls not canonicalizedModerate effort
Add a canonical tag to every product variant URL pointing back to the base product page, so Google consolidates ranking signals onto one authoritative URL instead of splitting them across hundreds of near-duplicate pages.
On Adobe Commerce (Magento)
- Adobe Commerce (Magento) generates configurable product URLs with parameters like ?color=45&size=12. By default, Magento may not canonicalise these back to the base product — this must be configured.
- In the Admin panel, go to Stores → Configuration → Catalog → Catalog → Search Engine Optimization.
- Set 'Use Canonical Link Meta Tag For Products' to 'Yes'. This tells Magento to output a canonical pointing to the base product URL on all product pages, including those loaded with option/attribute parameters.
- Also set 'Use Canonical Link Meta Tag For Categories' to 'Yes' to cover filtered category pages.
- Save the config, then flush the Magento cache: System → Cache Management → Flush Magento Cache.
- If you are on a PWA Studio or headless Adobe Commerce frontend, ensure your product page component reads the canonical_url field from the GraphQL product query and outputs it as <link rel="canonical" href={product.canonical_url}> in the document head.
- Verify with Google Search Console URL Inspection on a ?color= or ?size= variant URL.
Slow pageLarger project
Reduce page load time to under 3 seconds by compressing images, minifying CSS/JS, enabling caching, and improving server response speed.
On Adobe Commerce (Magento)
- Full-page cache: In Admin → System → Cache Management, ensure 'Full Page Cache' is enabled. Switch the FPC engine from the default built-in cache to Varnish (Admin → Stores → Configuration → Advanced → System → Full Page Cache → Caching Application → Varnish Cache) for dramatically faster response times.
- JS/CSS merging and minification: Go to Admin → Stores → Configuration → Advanced → Developer → JavaScript Settings and CSS Settings. Enable 'Merge JavaScript Files', 'Minify JavaScript Files', 'Merge CSS Files', and 'Minify CSS Files'. (Disable on staging first to check for conflicts.)
- Images: Install the 'TinyPNG' or 'WebP Images' extension from the Adobe Commerce Marketplace, or use a server-side tool (ImageMagick) to bulk-compress and convert product images. Ensure Catalog → Configuration → Product Image → Lazy Load is enabled.
- CDN: In Admin → Stores → Configuration → General → Web → Base URLs (Secure), configure a CDN base URL if using Fastly (included with Adobe Commerce Cloud) or a third-party CDN. For on-premise, install the Fastly extension or configure Cloudflare at the DNS level.
- Extensions: In Admin → System → Web Setup Wizard → Extension Manager (or Composer), audit installed modules. Disable unused third-party modules via bin/magento module:disable Vendor_Module to reduce boot overhead.
- Hosting: For on-premise/self-hosted Magento, enable PHP OPcache and Redis object/session caching via php.ini and app/etc/env.php respectively. Upgrade to PHP 8.x. For Adobe Commerce Cloud, open a support ticket to review Fastly and New Relic performance data.
Thin contentModerate effort
Expand thin pages to at least 300 words of unique, helpful content that genuinely describes your products, category, or topic.
On Adobe Commerce (Magento)
- Product pages: Admin → Catalog → Products → [select product] → 'Content' tab → expand 'Description' and write full content in the WYSIWYG editor. 'Short Description' appears near the price; 'Description' appears in the Details tab.
- Category pages: Admin → Catalog → Categories → [select category] → 'Content' tab → add text to the 'Description' field and optionally a CMS Block for richer content.
- For layered navigation (filter) pages generating thin URLs, go to Admin → Stores → Configuration → Catalog → Search Engine Optimization and set 'Use Categories Path for Product URLs' and canonical URL settings to consolidate duplicates. Also set layered navigation pages to noindex via the same SEO configuration panel or a third-party SEO extension.
Title too longQuick win
Shorten your page title tag to 60 characters or fewer so it displays in full — not truncated — in Google search results.
On Adobe Commerce (Magento)
- For a product: go to Catalog → Products → select the product → expand the 'Search Engine Optimization' section → edit the 'Meta Title' field.
- For a category: Catalog → Categories → select the category → 'Search Engine Optimization' tab → edit 'Meta Title'.
- For a CMS page: Content → Pages → select the page → expand 'Search Engine Optimization' section → edit 'Meta Title'.
- To set a global title suffix/prefix (which adds to every title and can push you over 60 chars): Stores → Configuration → Catalog → Catalog → Product Listings → 'Title Suffix' / 'Title Prefix' — keep these very short or remove them.
- For bulk updates across many products, use a CSV import: System → Data Transfer → Import → Entity Type: Products — populate the 'meta_title' column and import.
Title too shortQuick win
Expand your page title to between 50 and 60 characters so it displays fully in Google search results and gives shoppers a clear reason to click.
On Adobe Commerce (Magento)
- Log in to the Admin Panel and navigate to Catalog → Products (for products) or Catalog → Categories (for categories).
- Open the specific product or category.
- Expand the 'Search Engine Optimization' section in the edit form.
- Update the 'Meta Title' field with your new 50–60 character title.
- Click 'Save'. Clear your page cache (System → Cache Management → Flush Cache Storage) for the change to appear immediately.
Answer Engine Optimization · 15 fixes
Article missing authorModerate effort
Add a structured-data author field (schema.org Person) to every article and blog post so AI engines and search crawlers can verify who wrote the content.
On Adobe Commerce (Magento)
- If you use a blog extension (e.g. Magefan Blog, Aheadworks Blog, or Mirasvit Blog), locate the blog post view template — typically at app/design/frontend/<Vendor>/<theme>/Magefan_Blog/templates/post/view.phtml or the equivalent for your extension.
- Add a <script type='application/ld+json'> block inside that template that outputs an Article/BlogPosting schema with the 'author' Person object.
- Pull the author name from the post's author entity using the extension's available PHP/template variables (e.g. $post->getAuthor()->getName() in Magefan Blog).
- For sameAs, add a custom author attribute in the blog extension's admin (Magefan: Content → Blog → Authors → edit author → add Social URL field), then output it in the template.
- Run bin/magento cache:flush after saving, then verify with Google's Rich Results Test.
Invalid rating valueModerate effort
Fix the AggregateRating.ratingValue in your structured data so it falls within the valid numeric range (1–5 by default, or within the declared bestRating/worstRating bounds).
On Adobe Commerce (Magento)
- Structured data in Magento is typically output by the theme or a third-party module. Search your theme for 'ratingValue' under app/design/frontend/<Vendor>/<Theme>/.
- The default Luma theme generates rating schema in Magento_Review/templates/. Open the relevant .phtml file and locate the ratingValue output.
- Ensure the value is cast to float and clamped: e.g. max(1, min(5, (float)$block->getRatingSummary() / 20)) for a 0–100 summary score converted to 1–5.
- Add worstRating and bestRating properties to the same JSON-LD or microdata block.
- Run bin/magento cache:flush after saving template files, then validate with the Rich Results Test.
Low answer first scoreModerate effort
Restructure your page content to lead with a direct, concise answer and add FAQ schema so search engines and AI assistants can surface your content as a featured snippet or direct answer.
On Adobe Commerce (Magento)
- Navigate to Content → Pages (for CMS pages) or Catalog → Products → Edit → Content tab for product pages.
- In the Page Builder or WYSIWYG editor, place a Text row at the top of the content with your direct-answer summary paragraph.
- Use Heading elements (H2/H3) formatted as questions for each content section.
- For FAQ schema: paste the FAQPage JSON-LD <script> block into the page's HTML Source (switch the editor to HTML mode) at the bottom of the content, or add it via a CMS Static Block scoped to specific pages.
- For a site-wide or template-level solution, edit the relevant layout XML file (e.g., cms_page_view.xml or catalog_product_view.xml) to include a block that renders your FAQ JSON-LD for the appropriate page types.
- Flush the Magento cache (System → Cache Management → Flush Magento Cache) and verify with Google's Rich Results Test.
Low review countModerate effort
Collect at least 5 published reviews so Google can display star ratings in search results for your product pages.
On Adobe Commerce (Magento)
- In the Admin Panel go to Marketing → User Content → Reviews to see review counts per product and approve any pending reviews.
- Enable the native post-purchase review reminder: Stores → Configuration → Catalog → Product Reviews → 'Allow Guests to Write Reviews' and configure transactional email templates under Marketing → Email Templates.
- Adobe Commerce (and Magento Open Source) core themes (Luma/Blank) render aggregateRating schema automatically via the Magento_Review module once at least one approved review exists; the count increments automatically.
- For richer automation, install a review extension from the Adobe Commerce Marketplace (e.g. Yotpo, Trustpilot, or ReviewBooster).
- After reaching 5 reviews per product, run the Rich Results Test to confirm reviewCount ≥ 5 is present in the structured data output.
Missing schema articleModerate effort
Add Article and BreadcrumbList JSON-LD structured data to every editorial/blog page so search engines and AI answer engines can correctly identify, understand, and feature your content.
On Adobe Commerce (Magento)
- Adobe Commerce does not include a native blog, so most stores use an extension such as Magefan Blog or Aheadworks Blog — check which blog extension is installed first.
- For Magefan Blog: go to Admin › Magefan Blog › Configuration › SEO — enable structured data options if available in your extension version; newer versions auto-generate Article schema.
- For manual implementation, create or edit the blog post view template: app/design/frontend/<Vendor>/<Theme>/Magefan_Blog/templates/post/view.phtml (or the equivalent for your blog extension) and add the JSON-LD <script> blocks using PHP to output dynamic post data ($post->getTitle(), $post->getPublishTime(), etc.).
- For BreadcrumbList, edit the breadcrumb template or add JSON-LD alongside the existing visual breadcrumb, pulling the same crumb array your theme already builds.
- Clear the Magento cache (Admin › System › Cache Management › Flush Magento Cache) after changes, then validate with Google's Rich Results Test.
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 Adobe Commerce (Magento)
- SSH into your server or open your IDE and navigate to your active theme directory: app/design/frontend/<Vendor>/<theme>/.
- Create or edit the layout XML file for product pages (Magento_Catalog/layout/catalog_product_view.xml) and category pages (catalog_category_view.xml) to add a new block that renders your JSON-LD.
- Create a corresponding PHTML template (e.g. Magento_Catalog/templates/structured_data/breadcrumb.phtml) that uses Magento's breadcrumb data model ($block->getCrumbs()) to loop over the breadcrumb items and output a valid BreadcrumbList JSON-LD <script> block dynamically.
- Alternatively, install a structured data extension from the Adobe Commerce Marketplace (e.g. 'Structured Data' by Mageworx or similar) which auto-generates BreadcrumbList schema across all page types without custom development.
- Run bin/magento setup:upgrade && bin/magento cache:flush, then validate a product and category page in Google's Rich Results Test.
Missing schema faqpageModerate effort
Add FAQPage (and BreadcrumbList) JSON-LD structured data to pages that contain FAQ content so Google can display rich results directly in search.
On Adobe Commerce (Magento)
- SSH into your server or use your deployment pipeline to access theme files.
- Identify the relevant layout XML file for your page type (e.g. cms_page_view.xml for CMS pages, catalog_product_view.xml for product pages) inside app/design/frontend/<Vendor>/<theme>/Magento_Theme/layout/ (create the directory if needed).
- Add a block of type Magento\Framework\View\Element\Template that points to a new .phtml template file you create (e.g. templates/schema/faqpage.phtml).
- In that .phtml file, output your <script type='application/ld+json'> FAQPage JSON-LD. Use PHP variables from the block's data source (a custom block class or CMS field) to make question/answer content dynamic.
- For BreadcrumbList: Magento already has breadcrumb block logic; install or configure a structured data extension such as 'MageWorx SEO Suite' or 'Amasty SEO Toolkit' to output BreadcrumbList JSON-LD automatically alongside FAQPage.
- Run bin/magento cache:flush, then validate with Google's Rich Results Test.
Missing schema howtoModerate effort
Add HowTo and BreadcrumbList structured data (JSON-LD) to pages that contain step-by-step instructions so Google can display them as rich results and answer-engine snippets.
On Adobe Commerce (Magento)
- For CMS pages: go to Content › Pages › select the instructional page › Edit › Content tab.
- In the page content (HTML editor / Page Builder), add an HTML element and paste the <script type='application/ld+json'>…</script> block.
- For a more robust, maintainable approach: create a custom module or use an existing structured data extension (e.g. from Magento Marketplace, search 'structured data schema') to manage JSON-LD output via layout XML or a Block class.
- To inject via layout XML: add a block of type Magento\Framework\View\Element\Text to the <head> section in your theme's cms_page_view_id_{page_identifier}.xml layout file, with the JSON-LD as the block's text content.
- BreadcrumbList can be added the same way or generated dynamically by a schema extension that reads Magento's built-in breadcrumb trail.
- Validate with Google's Rich Results Test.
Missing schema localbusinessQuick win
Add a LocalBusiness JSON-LD schema block to your store so search engines and AI assistants can surface your business name, address, phone number, and hours in rich results and answer boxes.
On Adobe Commerce (Magento)
- In your Magento admin go to Content → Design → Configuration → select your store view → click Edit.
- Scroll to the 'HTML Head' section and locate the 'Scripts and Style Sheets' field.
- Paste your <script type='application/ld+json'>…</script> block there and Save Configuration.
- Alternatively, a developer can add the block to the default_head_blocks.xml layout file or create a custom module that outputs JSON-LD via a Block class for better maintainability.
- Flush the Magento cache (System → Cache Management → Flush Magento Cache) after saving.
Missing schema organizationQuick win
Add Organization schema markup to your store's homepage so search engines and AI systems can definitively identify your business, logo, and social profiles.
On Adobe Commerce (Magento)
- In your Magento Admin, go to Content → Design → Configuration.
- Select your store view and click 'Edit'.
- Under 'HTML Head', find the 'Scripts and Style Sheets' field.
- Paste your <script type='application/ld+json'>…</script> block there and save.
- Alternatively, create or edit your theme's default_head_blocks.xml layout file (located at app/design/frontend/<Vendor>/<theme>/Magento_Theme/layout/) and add the script via a block or directly in your default.xml <head> section.
- Flush the Magento cache (System → Cache Management → Flush Magento Cache) and validate with Google's Rich Results Test.
Missing schema productModerate effort
Add Product schema (JSON-LD structured data) to every product page so search engines can display rich results like price, availability, and ratings directly in search listings.
On Adobe Commerce (Magento)
- Adobe Commerce (Magento 2) includes basic Product structured data in its core Luma theme via the Magento_Catalog module — check whether your theme inherits from Luma or has overridden it.
- To verify current output: view source on a product page and search for 'application/ld+json' or 'Product' within a script tag.
- To customize or add missing fields: create a layout override in your custom theme at app/design/frontend/<Vendor>/<Theme>/Magento_Catalog/templates/product/view/structured_data.phtml (create this file if it doesn't exist, extending the core template).
- Alternatively, install a dedicated structured data extension from the Adobe Commerce Marketplace (e.g. 'SEO Suite Ultimate' by MageWorx or similar) which provides a full admin UI for managing Product schema fields without template editing.
- Flush the Magento cache: System → Cache Management → Flush Magento Cache, then validate a product URL with Google's Rich Results Test.
Missing schema webpageModerate effort
Add WebPage (and Organization) JSON-LD schema markup to every page so search engines and AI answer engines can confidently understand and describe your site's content.
On Adobe Commerce (Magento)
- For a code-based approach: create or edit the layout XML file for the relevant page type (e.g. cms_index_index.xml for homepage) in your custom theme under app/design/frontend/<Vendor>/<Theme>/Magento_Theme/layout/.
- Add a block of type 'Magento\Framework\View\Element\Template' pointing to a .phtml template file that outputs your JSON-LD <script> block, and reference it in the <head> section of the layout XML.
- In the .phtml file, use the Magento Block API or helper to dynamically populate 'name' (page title), 'url' (current URL), and 'description' (meta description) from the page's metadata.
- For a no-code approach, install a structured data extension from the Adobe Marketplace (e.g. 'Structured Data by Amasty' or 'JSON-LD Schema by Mageworx') which provides an admin UI to configure and auto-inject WebPage and Organization schema across all page types.
- Clear the Magento cache (System → Cache Management → Flush Cache) and validate using Google's Rich Results Test.
Non iso dateModerate effort
Change all datePublished (and dateModified) values in your structured data from informal formats like "09/23/2019 00:00:00" to the ISO-8601 format "2019-09-23" so search engines can correctly read your content's publication date.
On Adobe Commerce (Magento)
- In your Magento admin, go to Content → Configuration → check if a third-party SEO extension (e.g. Mageplaza SEO, Amasty SEO, Mageworx SEO Suite) is responsible for generating structured data — if so, consult that extension's settings panel for date format options.
- If structured data is output by a custom theme, navigate to your theme directory on the server (app/design/frontend/<Vendor>/<theme>/) and search for 'datePublished' or 'dateModified' in .phtml or .xml layout files.
- Locate the PHP code generating the date value and replace informal formatting with PHP's DateTime output: (new DateTime($date))->format(DateTime::ATOM) which produces a valid ISO-8601 string.
- Alternatively, use PHP's date() function: date('Y-m-d\TH:i:s\Z', strtotime($date_value)).
- Clear the Magento cache after changes: System → Cache Management → Flush Cache Storage.
- Test product and CMS pages in Google's Rich Results Test to confirm corrected date output.
Org missing sameasQuick win
Add a `sameAs` array to your Organization structured data, linking to your official social profiles, Wikipedia page, LinkedIn, and other authoritative directories so search engines can confidently identify your brand as a distinct entity.
On Adobe Commerce (Magento)
- In your Magento admin, go to Content → Design → Configuration → select your store view → Edit.
- Scroll to 'HTML Head' → 'Scripts and Style Sheets' and paste the Organization JSON-LD block (with `sameAs`) there, wrapped in `<script type='application/ld+json'>` tags.
- Alternatively, edit your theme's `default_head_blocks.xml` layout file to add a structured-data block via a custom module for more maintainable deployment.
- Flush the Magento cache (System → Cache Management → Flush Cache), then verify with Google's Rich Results Test.
Page unreachableModerate effort
Ensure every page that should appear in AI-powered answers and rich results is actually reachable by crawlers, then add structured data so search engines and AI systems can read and surface its content.
On Adobe Commerce (Magento)
- In the Admin panel, go to Catalog → Products (or CMS → Pages) and confirm the page/product is set to 'Enabled' and 'Visibility' is appropriate (e.g., 'Catalog, Search' for products).
- Check Stores → Configuration → General → Web → Search Engine Optimization — ensure 'Use Web Server Rewrites' is set to 'Yes' to avoid broken URLs.
- For 404 errors, go to Marketing → SEO & Search → URL Rewrites → Add URL Rewrite to create a custom 301 redirect from the broken path to the correct target.
- Check that important pages are not blocked in your robots.txt (Stores → Configuration → General → Design → HTML Head → robots field or the actual robots.txt file on the server root). Remove any Disallow rules for public pages.
- For structured data, Adobe Commerce includes basic Product schema. To extend it (e.g., add FAQPage schema, aggregate ratings), install a Marketplace extension such as 'MageWorx SEO Suite' or edit the relevant .phtml template files in your theme (e.g., app/design/frontend/[Vendor]/[Theme]/Magento_Catalog/templates/product/view/) to inject a JSON-LD script block.
- Flush the cache (System → Cache Management → Flush Magento Cache), then submit the URL in Google Search Console and validate with the Rich Results Test.
Accessibility (WCAG) · 63 fixes
Aria allowed attrModerate effort
Remove or replace any ARIA attributes that are not permitted on an element's assigned role so that assistive technologies can correctly interpret the element.
On Adobe Commerce (Magento)
- SSH into your server (or use your IDE connected to the server) and locate your active theme under `app/design/frontend/<Vendor>/<Theme>/`.
- Use `grep -r 'aria-<attribute-name>' app/design/` to find the template file (.phtml or .html for Knockout/UI components) containing the offending attribute.
- Open the file, correct or remove the disallowed ARIA attribute, and save. If the file does not exist in your custom theme yet, copy it from `vendor/magento/module-<ModuleName>/view/frontend/templates/` into your theme at the matching path first (template override pattern).
- If the attribute originates in a JavaScript/Knockout UI component, find the corresponding `.html` knockout template under `app/design/frontend/<Vendor>/<Theme>/<Module_Name>/web/template/` and apply the same override approach.
- Run `bin/magento setup:static-content:deploy` and `bin/magento cache:flush`, then re-test with axe DevTools.
Aria allowed roleModerate effort
Remove or replace the invalid `role="presentation"` (or other disallowed ARIA role) on HTML elements where that role is not permitted, so assistive technologies can correctly interpret your page.
On Adobe Commerce (Magento)
- Identify the offending template via the flagged page URL. Adobe Commerce uses `.phtml` template files; locate the relevant file in `app/design/frontend/<Vendor>/<Theme>/` (custom theme) or in the module's view directory.
- Use `grep -r 'role="presentation"' app/design/` (or the equivalent for your flagged role) from the project root to find the exact file and line.
- Edit the `.phtml` or `.html` (for UI Component / Knockout templates) file: remove the invalid role attribute from interactive elements or replace it with a permitted value.
- Run `bin/magento cache:flush` and `bin/magento setup:static-content:deploy` to push the changes to the storefront.
- Verify with axe DevTools on the frontend.
Aria command nameModerate effort
Add a discernible, screen-reader-accessible name to every button, link, and menuitem that uses an ARIA command role so assistive technology can announce what it does.
On Adobe Commerce (Magento)
- Identify the unlabelled element's source template by enabling Magento template hints (Stores → Configuration → Advanced → Developer → Debug → Enable Template Path Hints for Storefront).
- Copy the relevant .phtml template to your custom theme under app/design/frontend/<Vendor>/<theme>/ maintaining the same directory structure.
- Open the copied template and add `aria-label="Descriptive action"` to the `<button>` or `<a>` tag. For inline SVGs, add `aria-hidden="true"` and a visually-hidden `<span class="sr-only">Label</span>` inside the element.
- If using Knockout JS / UI Components (e.g., minicart), edit the corresponding .html knockout template in the same theme override path.
- Run `bin/magento cache:flush` and `bin/magento setup:static-content:deploy` then verify with axe DevTools on the storefront.
Aria dialog nameModerate effort
Add a descriptive accessible name to every dialog and alertdialog element using aria-label or aria-labelledby so screen-reader users know what the dialog is about.
On Adobe Commerce (Magento)
- Dialogs in Adobe Commerce (Magento 2) are typically rendered via the modal UI component (Magento_Ui/js/modal/modal) or custom .phtml templates.
- For the built-in modal component, override the template in your custom theme at app/design/frontend/<Vendor>/<Theme>/Magento_Ui/templates/modal/modal-popup.html (or modal-slide.html) and add aria-labelledby or aria-label to the outer <div role="dialog"> element.
- For custom .phtml popups, edit the template file directly and add the appropriate aria attribute to the dialog container.
- If the dialog title is rendered inside the modal, give that element a unique id and reference it with aria-labelledby on the dialog wrapper.
- Run bin/magento setup:static-content:deploy after changes, clear cache (bin/magento cache:flush), then verify with axe DevTools.
Aria hidden focusModerate effort
Remove `aria-hidden="true"` from any element that contains focusable children (links, buttons, inputs), or remove the focusable elements from inside the hidden container.
On Adobe Commerce (Magento)
- Identify the affected template file by running the axe DevTools extension and noting the element's surrounding markup — then use `grep -r 'aria-hidden' app/design/` in your project root to locate the source template.
- The file is most likely in `app/design/frontend/<Vendor>/<Theme>/Magento_Theme/templates/`, `Magento_Catalog/templates/`, or a similar module template directory.
- Open the file, locate the `aria-hidden="true"` container wrapping a focusable element, and apply the fix (remove attribute, move control outside, or restrict to decorative icon).
- If the markup comes from a third-party module, create a template override in your custom theme directory rather than editing the module directly, so upgrades don't revert your fix.
- Run `bin/magento cache:clean` and `bin/magento setup:static-content:deploy` then re-test on the frontend with axe DevTools.
Aria input field nameModerate effort
Add a meaningful accessible name (label) to every ARIA input field so screen readers can identify and announce it to users.
On Adobe Commerce (Magento)
- Locate the PHTML or HTML template responsible for the input field — search under app/design/frontend/<Vendor>/<Theme>/ (custom theme) or vendor/magento/module-*/view/frontend/templates/ (core module).
- Override the template by copying it to your custom theme directory under app/design/frontend/<Vendor>/<Theme>/Magento_<ModuleName>/templates/ preserving the path.
- In the overridden template file, add <label for="field-id">Descriptive name</label> linked to the input's id, or add aria-label="Descriptive name" directly on the <input> or ARIA-role element.
- For UI Component-based forms (e.g., checkout), edit the XML layout file in app/design/frontend/<Vendor>/<Theme>/Magento_Checkout/layout/checkout_index_index.xml and add an 'additionalClasses' or 'label' node, or use a JavaScript mixin to add the aria-label attribute at runtime.
- Run bin/magento cache:clean && bin/magento setup:static-content:deploy, then verify with the axe browser extension on the frontend.
Aria meter nameModerate effort
Add a descriptive accessible name (via aria-label, aria-labelledby, or a visible <label>) to every element that uses role="meter" so screen readers can announce what the meter represents.
On Adobe Commerce (Magento)
- Locate the rating/meter template in your theme: typically `app/design/frontend/<Vendor>/<theme>/Magento_Review/templates/` — files like `rating.phtml` or `view/frontend/templates/product/view/list.phtml`.
- If you have not already, create a theme override by copying the core template into your custom theme directory rather than editing core files.
- Open the template and find the `<meter>` or `role="meter"` element. Add `aria-label="<?= $block->escapeHtml(__('Average customer rating')) ?>"`.
- Run `bin/magento cache:flush` and `bin/magento setup:static-content:deploy`, then test with axe DevTools on the frontend.
Aria progressbar nameQuick win
Add a descriptive accessible name to every progress bar element so screen readers can announce what it represents.
On Adobe Commerce (Magento)
- Locate the template file that renders the progress bar. Common locations: `app/design/frontend/<Vendor>/<theme>/Magento_Checkout/templates/progress-bar.phtml` (checkout steps), or a custom module template.
- Open the `.phtml` file and add `aria-label="<?= $escaper->escapeHtmlAttr(__('Checkout progress')) ?>"` (or `aria-labelledby`) to the element tag. Use Magento's `__()` translation function so the label is translatable.
- For the password-strength meter (`Magento_Customer` module), override `app/design/frontend/<Vendor>/<theme>/Magento_Customer/templates/widget/password/strength_indicator.phtml` in your custom theme and add the `aria-label` attribute.
- Run `bin/magento setup:static-content:deploy` and `bin/magento cache:flush` after saving template changes.
- Verify in the browser Accessibility panel and with an automated scan (e.g., axe DevTools extension).
Aria prohibited attrModerate effort
Remove or replace ARIA attributes that are explicitly prohibited on an element's assigned role, ensuring every ARIA attribute used is valid for its context.
On Adobe Commerce (Magento)
- SSH into your server (or use your IDE) and locate the relevant PHTML or HTML template file in your custom theme under `app/design/frontend/<Vendor>/<Theme>/`.
- Search the theme directory for the prohibited attribute string: `grep -r 'aria-label\|aria-checked' app/design/frontend/<Vendor>/<Theme>/` (adjust the attribute name as needed).
- Open the relevant `.phtml` or `.html` (Knockout/Magento UI component) template and remove or correct the prohibited ARIA attribute.
- If the attribute comes from a third-party module, override its template in your custom theme rather than editing the module directly.
- Run `bin/magento cache:flush` and `bin/magento setup:static-content:deploy` to push the changes to the frontend.
- Verify the fix with the axe DevTools browser extension on the affected page.
Aria required attrModerate effort
Add the required `aria-level` attribute (and any other missing required ARIA attributes) to every element that uses an ARIA role which mandates them.
On Adobe Commerce (Magento)
- Identify the template file responsible for the flagged element: use the browser's DevTools to find a unique class or string, then search your theme under `app/design/frontend/<Vendor>/<Theme>/` using `grep -r 'role=' .`
- Copy the core template into your custom theme following Magento's template override convention (mirror the module path under your theme directory) to avoid editing core files.
- In the overridden `.phtml` template, add the missing required attribute to the HTML element, e.g., `<div role="heading" aria-level="2">`.
- If the violation is in a UI Component (`.html` KnockoutJS template), locate it under `Magento_*/view/frontend/web/template/` and apply the same override pattern.
- Run `bin/magento cache:clean` and `bin/magento setup:static-content:deploy`, then verify with axe DevTools.
Aria required childrenModerate effort
Ensure every ARIA parent role contains only its required, permitted child roles — and remove focusable elements (e.g. tabindex on img or a) that are not allowed inside that ARIA context.
On Adobe Commerce (Magento)
- Identify the affected template file using Magento's template hints (Stores → Configuration → Advanced → Developer → Debug → Enable Template Path Hints for Storefront).
- Copy the flagged template from vendor/magento/module-[ModuleName]/view/frontend/templates/ into your custom theme at app/design/frontend/[Vendor]/[Theme]/Magento_[ModuleName]/templates/ — never edit core files directly.
- Open the copied .phtml file, locate the element with the parent role= attribute, and correct the child role pairings or remove disallowed tabindex attributes from img/a children.
- If the violation is in a custom module or third-party extension template, copy it into your theme override path following the same pattern and correct it there.
- Run bin/magento cache:flush after saving. Verify the fix in your browser with axe DevTools and check the Accessibility panel in Chrome DevTools.
Aria required parentModerate effort
Wrap every ARIA child role (such as `tab`, `option`, `listitem`, `row`, etc.) in the correct required ARIA parent container role (such as `tablist`, `listbox`, `list`, `rowgroup`, or `grid`) so assistive technologies can correctly interpret the widget's structure.
On Adobe Commerce (Magento)
- SSH into your server (or use your hosting file manager) and locate the PHTML template responsible for the widget — e.g., `app/design/frontend/<Vendor>/<theme>/Magento_Catalog/templates/product/view/details.phtml` for product tabs.
- Create a theme override if one doesn't exist by copying the core template into your custom theme directory.
- In the template, locate the elements with child ARIA roles (e.g., `role="tab"`) and wrap them in a container with `role="tablist"`.
- If the widget is rendered by a JavaScript UI component (Knockout.js/RequireJS), update the relevant `.html` knockout template under `web/template/` in your module.
- Run `bin/magento cache:flush` and `bin/magento setup:static-content:deploy`, then test the live page with Chrome Accessibility Inspector.
Aria rolesModerate effort
Audit every element that has a `role` attribute and replace any invalid, misspelled, or non-existent ARIA role value with a valid WAI-ARIA role from the official specification.
On Adobe Commerce (Magento)
- Via SSH or SFTP, navigate to your theme directory: `app/design/frontend/<Vendor>/<theme>/`.
- Search all `.phtml` and `.html` (Knockout/UI Component) template files for `role=` using: `grep -r 'role=' app/design/frontend/<Vendor>/<theme>/`
- Also search the base Luma/Blank theme at `vendor/magento/theme-frontend-luma/` or `vendor/magento/theme-frontend-blank/` for the original template, then override it in your custom theme rather than editing core files.
- Fix invalid role values in the relevant template file within your custom theme folder, or create a template override if editing a core/vendor template.
- For issues in JavaScript UI components (`.html` KO templates under `Magento_*/view/frontend/web/template/`), locate and override the component template in your theme.
- Run `bin/magento cache:clean` and `bin/magento setup:static-content:deploy`, then verify with axe DevTools.
Aria toggle field nameModerate effort
Give every toggle control (checkbox, switch, or ARIA toggle button) a descriptive accessible name so screen readers can announce what it does.
On Adobe Commerce (Magento)
- Identify the .phtml template or UI Component XML file rendering the toggle. Common locations: app/design/frontend/<Vendor>/<Theme>/Magento_Checkout/ for checkout toggles, or a custom module's view/frontend/templates/ directory.
- For <input type='checkbox'>, ensure a <label for='...'> with matching 'for' and 'id' values is present in the template.
- For custom JavaScript toggle widgets (e.g. using Magento's UI Component or KnockoutJS bindings), add an aria-label binding: <button aria-label='...' data-bind="attr: {'aria-pressed': isActive}">.
- Clear the Magento cache after changes: in the admin go to System → Cache Management → Flush Magento Cache, or run bin/magento cache:flush from the CLI.
- Re-run an axe scan on the affected page to confirm the accessible name is detected.
Aria tooltip nameModerate effort
Add a visible, descriptive text label to every element that has role="tooltip" so screen readers can announce its purpose.
On Adobe Commerce (Magento)
- Tooltip elements in Adobe Commerce are typically rendered in PHTML templates or JavaScript UI components (Knockout.js). Search your theme's app/design/frontend/<Vendor>/<theme>/ directory for 'role="tooltip"' or 'data-tooltip' attributes.
- For PHTML-based tooltips, open the relevant template file (common locations: Magento_Catalog/templates/product/, Magento_Ui/templates/) and add an aria-label attribute or visible text content to the tooltip element.
- For JavaScript/Knockout-rendered tooltips (common in checkout, forms, or product configurators), locate the corresponding .html Knockout template file and add aria-label using Knockout binding: <div role="tooltip" data-bind="attr: { 'aria-label': tooltipText }"><!-- ko text: tooltipText --><!-- /ko --></div>.
- If using a third-party extension for tooltips (e.g., Amasty, MageWorx), check the extension's admin configuration panel under Stores → Configuration for label/accessibility text fields, or override its template in your custom theme.
- Run bin/magento setup:static-content:deploy and bin/magento cache:flush after template changes, then test with a screen reader or axe browser extension.
Aria valid attrModerate effort
Find every misspelled or non-existent ARIA attribute name in your HTML (e.g. `aria-labeledby` instead of `aria-labelledby`) and correct each one to a valid ARIA attribute name.
On Adobe Commerce (Magento)
- Identify the affected template file using your browser's DevTools (right-click the element → Inspect) and note the surrounding HTML context to search for it in your codebase.
- In your Magento installation, search your active theme directory (`app/design/frontend/<Vendor>/<Theme>/`) for the misspelled attribute using a code editor or CLI: `grep -r 'aria-labeledby' app/design/`.
- Also search core module templates that may have been copied as overrides: `grep -r 'aria-labeledby' vendor/magento/`.
- Open the relevant `.phtml` or `.html` (Knockout/UI Component) file, correct the attribute name, and save.
- If the error is in a third-party extension, check for an updated version on the vendor's site or Adobe Commerce Marketplace.
- Run `bin/magento cache:flush`, then verify with axe DevTools in a browser.
Aria valid attr valueModerate effort
Audit every ARIA attribute on your pages and correct any that point to a non-existent element ID, use a disallowed value, or reference an empty/misspelled target so that assistive technologies can correctly interpret your page.
On Adobe Commerce (Magento)
- Identify the PHTML template or JavaScript UI component responsible for the failing element using browser DevTools (right-click → Inspect to see the element, then trace to the template via layout XML).
- Locate the file in your custom theme under app/design/frontend/<Vendor>/<Theme>/ — copy the core file into your theme directory if it does not already exist there (never edit core files directly).
- Open the PHTML or .js file in your code editor and find the ARIA attribute; fix the ID reference or keyword value.
- For JavaScript-rendered components (e.g., Knockout.js or React widgets), search the corresponding .js or .html knockout template file for the attribute binding and correct it.
- Run bin/magento cache:clean && bin/magento cache:flush after saving, then reload the page and verify with the axe DevTools browser extension.
Autocomplete validModerate effort
Add a valid, correctly matched `autocomplete` attribute to every personal-data form field so browsers and assistive technologies can autofill them reliably.
On Adobe Commerce (Magento)
- Autocomplete attributes live in the PHTML template files for each form (checkout, customer registration, address book).
- To customise safely, create a theme override: copy the relevant template from vendor/magento/module-checkout/view/frontend/templates/ (e.g., shipping-address/form.phtml) or module-customer into app/design/frontend/Vendor/Theme/ preserving the module path.
- Open the copied PHTML file, locate each <input> element, and add or correct the autocomplete attribute using the correct HTML token.
- For the Checkout page, the shipping and billing address forms are rendered via Knockout JS UI components; edit the corresponding .html template in Magento_Checkout/web/template/shipping-address/ or billing-address/ within your theme.
- Run bin/magento cache:flush and bin/magento setup:static-content:deploy, then verify the rendered HTML contains the correct autocomplete values.
Avoid inline spacingModerate effort
Remove hard-coded text-spacing CSS properties from inline `style` attributes so users can override them with their own stylesheets.
On Adobe Commerce (Magento)
- Identify the template file responsible: check `app/design/frontend/<Vendor>/<Theme>/` for `.phtml` files or `.html` Knockout/UI-component templates that output `style=` with spacing properties.
- In your custom child theme, create an override of the offending template (copy it to `app/design/frontend/<YourVendor>/<YourTheme>/`) and remove the inline spacing property from the `style` attribute.
- Add a CSS class to the element and define the spacing in `web/css/source/_module.less` or `web/css/source/_extend.less` under that class.
- If spacing is injected via a CMS block or Page Builder widget (in Commerce), go to Content → Blocks or Content → Pages, edit the block, switch to HTML view, find and remove the inline `style` spacing property, and instead apply a CSS class.
- Run `bin/magento setup:static-content:deploy` and clear the cache (`bin/magento cache:flush`) to deploy updated styles.
- Verify with axe DevTools.
Button nameModerate effort
Add a visible or programmatically accessible name to every button so screen readers can announce what it does.
On Adobe Commerce (Magento)
- In your custom theme directory (app/design/frontend/<Vendor>/<theme>/), locate the relevant .phtml template — common files: Magento_Checkout/templates/cart/minicart.phtml, Magento_Search/templates/form.mini.phtml, Magento_Catalog/templates/product/view/addtocart.phtml.
- Find the <button> element and add aria-label="Descriptive name" as an HTML attribute, or insert a <span class="amasty-sr-only"> / <span class="action"> element with visually-hidden CSS.
- If using UI Components or Knockout.js templates (*.html in web/template/ directories), add the aria-label binding: <button ... attr="aria-label: 'Open cart'">.
- Run bin/magento cache:clean after saving template changes, then verify with axe DevTools in the browser.
Color contrastModerate effort
Increase the contrast ratio between your text color and its background color to at least 4.5:1 so all users — including those with low vision — can read your content.
On Adobe Commerce (Magento)
- Identify the failing CSS rule by inspecting the element in browser DevTools (F12) — note the stylesheet filename and rule selector.
- In your custom theme directory, locate or create the corresponding LESS/CSS file under app/design/frontend/<Vendor>/<theme>/web/css/.
- Override or add the rule with the passing color value, e.g.: `a { color: #005f8e; }`. Avoid editing core or vendor files directly.
- Run `bin/magento setup:static-content:deploy` and clear the cache (`bin/magento cache:clean`) to apply the changes.
- Verify with Lighthouse or axe DevTools in the browser.
Definition listModerate effort
Fix all `<dl>` (definition list) elements so they contain only valid `<dt>` and `<dd>` child elements, in the correct order, with no stray tags or text directly inside the list wrapper.
On Adobe Commerce (Magento)
- Identify the template file rendering the `<dl>` by using browser DevTools to inspect the element and noting the Magento layout handle or block name (often visible in layout XML comments when developer mode is on).
- Locate the PHTML template file — common paths include 'app/design/frontend/<Vendor>/<theme>/Magento_Catalog/templates/product/view/' or a custom module's 'view/frontend/templates/' directory.
- Edit the PHTML file to correct the `<dl>` structure: ensure only `<dt>`, `<dd>`, or wrapping `<div>` elements are direct children. Create the file in your custom theme directory to avoid overwriting on upgrades.
- Clear the Magento cache: in Admin go to System → Cache Management → Flush Magento Cache, or run 'bin/magento cache:flush' via CLI.
- Deploy static content if needed ('bin/magento setup:static-content:deploy'), then verify the fix on the frontend with an accessibility scanner.
DlitemQuick win
Wrap every `<dt>` (term) and `<dd>` (description) element inside a parent `<dl>` element so screen readers can correctly announce the list structure.
On Adobe Commerce (Magento)
- Connect to your server via FTP/SFTP or use the admin's built-in template tools (if enabled).
- Search your active theme directory (`app/design/frontend/Vendor/ThemeName/`) for files containing `<dt` or `<dd`. Common files include `Magento_Catalog/templates/product/view/attributes.phtml` and any custom `.phtml` snippets.
- To override a core template safely, copy `vendor/magento/module-catalog/view/frontend/templates/product/view/attributes.phtml` to `app/design/frontend/Vendor/ThemeName/Magento_Catalog/templates/product/view/attributes.phtml`.
- Open the copied file, locate any `<dt>` or `<dd>` elements without a `<dl>` parent, and add the `<dl>` wrapper around them.
- Run `bin/magento cache:flush` from the command line, then test on a product page using axe DevTools.
Document titleQuick win
Add a unique, descriptive <title> element to every page so browsers, screen readers, and search engines can identify it.
On Adobe Commerce (Magento)
- For store-level default title suffix: Admin → Stores → Configuration → General → Web → Default Page Title → fill in the 'Title Prefix' and 'Title Suffix' fields to append your store name globally.
- For the homepage: Content → Pages → find 'Home Page' → Edit → expand 'Search Engine Optimization' → fill in 'Page Title'.
- For CMS pages: Content → Pages → [select page] → Edit → 'Search Engine Optimization' accordion → 'Page Title'.
- For product pages: Catalog → Products → [select product] → 'Search Engine Optimization' tab → 'Meta Title' field.
- For category pages: Catalog → Categories → [select category] → 'Search Engine Optimization' tab → 'Meta Title'.
- To fix missing titles across many products, use the Product Grid bulk edit: Catalog → Products → select products → Actions → Update Attributes → set Meta Title.
Empty headingModerate effort
Find every empty heading tag on your store and add meaningful, visible text to it — or remove the tag entirely if it serves no structural purpose.
On Adobe Commerce (Magento)
- SSH into your server (or use your deployment pipeline) and search theme template files under app/design/frontend/<Vendor>/<theme>/ for heading tags: `grep -rn '<h[1-6][^>]*></h[1-6]>' .`
- Open each identified .phtml or .html (Knockout/Hyva) template file in your editor.
- For hardcoded empty headings, add meaningful text or remove the tag. For dynamically-output headings, wrap them in a condition: `<?php if ($block->getTitle()): ?><h2><?= $block->escapeHtml($block->getTitle()) ?></h2><?php endif; ?>`
- In the Magento Admin, go to Content → Pages or Content → Blocks, open the relevant CMS page/block in the WYSIWYG editor, switch to HTML source view, find empty `<h>` tags, and fix or remove them.
- Clear the Magento cache: System → Cache Management → Flush Magento Cache, then re-test with axe DevTools.
Empty table headerModerate effort
Add descriptive text to every table header cell (`<th>`) so that screen readers can announce what each column or row represents.
On Adobe Commerce (Magento)
- Tables in Adobe Commerce appear in product listing pages, comparison pages (`catalog/product/compare/list.phtml`), cart (`checkout/cart/item/default.phtml`), and order history templates.
- SSH into your server or use your IDE to open the relevant `.phtml` template file inside your custom theme directory: `app/design/frontend/<Vendor>/<Theme>/`.
- Search for `<th` tags that have no text content between the opening and closing tag. Add a descriptive text label or `aria-label` attribute to each.
- Common locations: `Magento_Catalog/templates/product/list.phtml`, `Magento_Checkout/templates/cart/item/default.phtml`, `Magento_Catalog/templates/product/compare/list.phtml`.
- After editing, run `bin/magento cache:clean && bin/magento cache:flush` from the CLI to clear the cache.
- Re-test with axe DevTools or WAVE on the storefront to confirm all table headers now have accessible names.
Frame titleQuick win
Add a descriptive `title` attribute to every `<iframe>` element on your store so screen readers can identify the frame's content.
On Adobe Commerce (Magento)
- Identify which template file renders the iframe. Navigate to app/design/frontend/<Vendor>/<Theme>/ in your theme (or app/code/<Module>/view/frontend/templates/ for a module).
- Open the relevant .phtml template file (e.g. a CMS page template, a video widget template, or a checkout template) in your code editor or via the server file system.
- Locate the `<iframe>` tag and add `title="Your descriptive label"` as an attribute. For CMS page/block iframes: go to Admin → Content → Pages (or Blocks) → Edit → switch to HTML source view → find the iframe and add the title attribute, then save.
- Clear the Magento cache: go to Admin → System → Cache Management → 'Flush Magento Cache', or run `bin/magento cache:flush` via CLI.
- For iframes injected by third-party Magento extensions, check the extension's admin configuration under Stores → Configuration → [Extension Name] for an accessibility or iframe title setting. If unavailable, create a small layout XML override or a plugin to append the attribute.
- Verify in browser DevTools on the live/staging store that every iframe has a descriptive, non-empty title attribute.
Heading orderModerate effort
Fix heading tags so they follow a logical, sequential order (H1 → H2 → H3…) without skipping levels anywhere on the page.
On Adobe Commerce (Magento)
- Identify the affected template: use browser DevTools to find the heading element, then trace it to its .phtml template file in your theme under app/design/frontend/<Vendor>/<Theme>/.
- Copy the core template into your custom theme (never edit core files) and change the heading tag to the correct sequential level.
- For CMS pages and blocks (Content → Pages or Content → Blocks), open the page in the editor, click 'Show/Hide Editor' to switch to HTML view, and correct any heading tags.
- If using Page Builder (Adobe Commerce 2.4+), open the page in Content → Pages, click Edit with Page Builder, select the heading element, and change its 'Heading Type' from the dropdown to the correct level.
- Run bin/magento cache:flush after template changes, then re-test with the axe DevTools extension.
Html has langQuick win
Add a valid `lang` attribute to the `<html>` element so browsers and assistive technologies know what language your page is written in.
On Adobe Commerce (Magento)
- The root `<html>` tag is declared in `vendor/magento/module-theme/view/base/templates/root.phtml` — do NOT edit the core file.
- Instead, override it in your custom theme: copy the file to `app/design/frontend/<Vendor>/<Theme>/Magento_Theme/templates/root.phtml`.
- In the copied file, locate the `<html` tag. Magento outputs the language via a helper; ensure the tag includes `lang="<?= $htmlAttributes ?>"` or check that the `html_attributes` block renders the `lang` attribute correctly.
- Alternatively, in your theme's `default_head_blocks.xml` layout file you can add a `<html>` attribute node to inject the lang attribute dynamically using the store's configured locale.
- Set the store locale under Stores → Configuration → General → General → Locale Options → Locale.
- Run `bin/magento cache:flush`, then verify via View Source on the storefront.
Html lang validQuick win
Set a valid BCP 47 language code on the `lang` attribute of your page's `<html>` element (e.g., `lang="en"`) so browsers, screen readers, and search engines correctly identify the page language.
On Adobe Commerce (Magento)
- The `lang` attribute is output by the root layout XML and the HTML head block. In your custom theme, open or create `app/design/frontend/<Vendor>/<Theme>/Magento_Theme/layout/default_head_blocks.xml`.
- Alternatively, find your theme's root template, typically `app/design/frontend/<Vendor>/<Theme>/Magento_Theme/templates/root.phtml`.
- In `root.phtml`, locate the `<html` tag and ensure it reads: `<html lang="<?= $htmlTag->getLang() ?>" ...>` — this uses Magento's built-in locale resolver.
- If you need a static fallback, set the store's locale in Admin → Stores → Configuration → General → General → Locale Options → Locale. Magento maps this to the `lang` attribute automatically when using the default template.
- Run `php bin/magento cache:flush` and `php bin/magento setup:static-content:deploy`, then verify the rendered `lang` attribute in page source.
Image altModerate effort
Add a descriptive `alt` attribute to every `<img>` element on your store so screen readers and search engines can understand what each image shows.
On Adobe Commerce (Magento)
- **Product images:** Admin → Catalog → Products → select a product → scroll to the 'Images and Videos' section → hover over an image → click the gear icon → fill in the 'Alt Text' field → Save.
- **CMS page / block images:** Admin → Content → Pages (or Blocks) → edit a page → in the WYSIWYG editor, click an image → click 'Insert Image' properties (the image icon) → fill in the 'Image Alt Text' field → Insert.
- **Theme template files:** Via SSH or file manager, locate the relevant `.phtml` template (e.g., `app/design/frontend/<Vendor>/<theme>/Magento_Catalog/templates/product/image.phtml`) and ensure the `alt` attribute is output using the product's `$block->getImageAlt()` or similar helper.
- **Bulk import:** Admin → System → Import → Entity Type: Products → download a sample CSV, fill in the `image_label` column (which maps to image alt text for the base image), and re-import.
- **Magento Open Source / Adobe Commerce CLI:** Use a data patch or upgrade script to programmatically set the `image_label` attribute on existing product images in bulk via the product repository.
Image redundant altModerate effort
Remove or empty the alt attribute on images whose caption or surrounding text already describes them, so screen readers don't announce the same information twice.
On Adobe Commerce (Magento)
- For product images: go to Admin → Catalog → Products → select the product → Images and Videos section → hover over the image → click the settings icon (pencil) → clear the 'Alt Text' field → Save.
- For CMS page or block images added via the WYSIWYG editor: Admin → Content → Pages (or Blocks) → select the record → Edit → click the image in the editor → click 'Image Properties' → clear the 'Image Description' (alt) field → Insert → Save.
- For images hard-coded in theme templates: locate the relevant .phtml or .html template under app/design/frontend/<Vendor>/<theme>/ → find the <img> tag → set alt="" or use an empty string variable → deploy static content.
- For widget images: Admin → Content → Widgets → select the widget → find the image settings and clear the alt text field.
Input button nameQuick win
Add a descriptive label to every input button so screen readers can announce what the button does.
On Adobe Commerce (Magento)
- Identify the relevant `.phtml` template file. Common locations: `app/design/frontend/<Vendor>/<Theme>/Magento_Checkout/templates/`, `Magento_Catalog/templates/product/view/`, or `Magento_Customer/templates/`.
- Create a child theme override if one does not already exist so you do not edit core files (place overrides under `app/design/frontend/<Vendor>/<Theme>/`).
- Open the template file, find the `<input type="submit"` or `<input type="button"` tag with an empty `value`, and add a descriptive label using the translation helper: e.g., `value="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"`.
- Run `bin/magento cache:flush` and `bin/magento setup:static-content:deploy` after saving.
- Verify in the browser with an accessibility inspector or screen reader that the button's accessible name is now correctly announced.
LabelModerate effort
Add a visible or programmatic label to every form input so assistive technologies can identify its purpose.
On Adobe Commerce (Magento)
- Create or edit a template override in your custom theme directory (app/design/frontend/Vendor/theme/).
- Locate the relevant .phtml template — e.g., Magento_Customer/templates/form/login.phtml, Magento_Checkout/templates/form/field.phtml.
- Add <label for="INPUT_ID"> markup above unlabeled inputs; use the class 'sr-only' (visually hidden via CSS) if design requires hiding the label visually while keeping it accessible.
- Run bin/magento setup:static-content:deploy and clear the cache (bin/magento cache:flush) after saving template changes.
- Audit all custom module forms and third-party extension templates for the same issue, then verify with axe DevTools.
Label title onlyModerate effort
Add a visible, persistent label to every form field so it is never labeled only by a tooltip (title) or hidden description (aria-describedby).
On Adobe Commerce (Magento)
- Identify the PHTML template responsible for the flagged form field. Common locations: `app/design/frontend/VENDOR/THEME/Magento_Checkout/templates/form/` (checkout), `Magento_Customer/templates/form/` (login/register), `Magento_Search/templates/` (search).
- Copy the core template into your custom theme's folder (following Magento's template override convention) so you do not edit core files directly.
- In the copied template, locate the `<input>` element. Add `<label for="FIELD_ID">Visible Label Text</label>` before it and ensure the input has `id="FIELD_ID"`. For design-constrained fields (e.g., the top search bar), add `aria-label="Search"` directly on the `<input>` and remove the `title`-only attribute.
- Run `bin/magento cache:clean` and `bin/magento setup:static-content:deploy` after saving template changes.
- Verify in a staging environment with an accessibility scanner before deploying to production.
Landmark banner is top levelModerate effort
Ensure your site's banner landmark (<header> or role="banner") sits at the top level of the page, not nested inside another landmark region.
On Adobe Commerce (Magento)
- The banner/header landmark is defined in your active theme's layout XML and template (PHTML) files. Navigate to `app/design/frontend/<Vendor>/<Theme>/Magento_Theme/templates/html/` in your theme directory.
- Open `header.phtml`. Confirm the `<header>` element is rendered at the top of the page structure — check the root template `root.phtml` (or `1column.phtml` / `2columns-left.phtml` etc.) to see the full page wrapper.
- In the root template, ensure `<?= $block->getChildHtml('header') ?>` is called as a direct child of `<body>` (or a non-landmark wrapper `<div>`), not inside a `<main>`, `<section>`, `<article>`, or `<aside>`.
- If a custom layout XML is wrapping the header inside a landmark container, edit `Magento_Theme/layout/default.xml` in your theme to restructure the container order.
- Run `bin/magento cache:flush` after making changes, then test with axe DevTools on the storefront.
Landmark complementary is top levelModerate effort
Move any `<aside>` element (or element with `role="complementary"`) so it is a direct child of `<body>`, not nested inside another landmark region like `<main>`, `<header>`, `<footer>`, or `<nav>`.
On Adobe Commerce (Magento)
- Identify the layout XML file responsible for the page type (e.g. `catalog_product_view.xml`, `cms_index_index.xml`) in your custom theme under `app/design/frontend/<Vendor>/<Theme>/Magento_Theme/layout/` or the relevant module's layout directory.
- Open the layout XML and find the block or container that renders the sidebar (commonly `catalog.leftnav` or a `sidebar.main` container). Check which container wraps it — if it is inside the `content` container (which maps to `<main>`), it is incorrectly nested.
- Move the sidebar container reference in the XML to be a sibling of `content` rather than a child, for example as a direct child of the `page.wrapper` or root container.
- In the corresponding PHTML template or `default.xml`, update the CSS grid/flex classes on the outer wrapper so the visual layout is preserved.
- Run `bin/magento cache:flush` and `bin/magento setup:static-content:deploy`, then re-test with axe DevTools.
Landmark contentinfo is top levelModerate effort
Move your footer element (or any element with role="contentinfo") to the top level of the page so it is not nested inside another landmark region.
On Adobe Commerce (Magento)
- In your Magento admin, go to Content → Design → Configuration → edit your active theme → HTML Head / Footer settings to inspect global footer markup.
- For theme-level fixes, locate your theme's `Magento_Theme/templates/html/footer.phtml` (or the equivalent file in your custom theme directory under `app/design/frontend/<Vendor>/<Theme>/`).
- Open your layout XML file — typically `Magento_Theme/layout/default.xml` — and confirm the `<footer>` block is declared as a direct child of `<body>`, not inside the `<main>` block.
- If the footer block is assigned as a child of `main` in the layout XML, move its `<referenceBlock>` declaration so it references `root` or the body-level container instead.
- Clear the Magento cache (System → Cache Management → Flush Cache Storage) and validate with axe DevTools.
- If using Page Builder, inspect the generated HTML in the browser and adjust the Page Builder layout to ensure the Footer row/section is outside the Main Content container.
Landmark main is top levelModerate effort
Move the `<main>` element (or `role="main"`) so it is a direct child of `<body>` and not nested inside any other landmark element such as `<header>`, `<nav>`, `<aside>`, or `<footer>`.
On Adobe Commerce (Magento)
- Locate your active theme folder under `app/design/frontend/<Vendor>/<theme>/`.
- Open `Magento_Theme/templates/root.phtml` (or the equivalent layout file for your theme) and inspect where `<main>` or `role="main"` is placed in the DOM hierarchy.
- Also review `Magento_Theme/layout/default.xml` and any layout XML that injects blocks into the page shell to find if a container wrapping `<main>` inadvertently has a landmark role.
- Edit the template/layout to ensure the `<main>` element is a direct child of `<body>`, not enclosed in another landmark. Run `bin/magento cache:clean` after changes.
- Test the fix on the frontend using axe DevTools browser extension.
Landmark no duplicate bannerModerate effort
Ensure your page has only one banner landmark (a single `<header>` element or `role="banner"`) so assistive technologies can navigate your site correctly.
On Adobe Commerce (Magento)
- SSH or FTP into your server and navigate to your active theme directory: app/design/frontend/<Vendor>/<theme>/Magento_Theme/templates/ (or the equivalent path in the default Luma/Blank theme at vendor/magento/theme-frontend-luma).
- Open root.phtml (or html/head.phtml, default.phtml). Search for '<header' and 'role="banner"' across all layout XML and template .phtml files.
- Ensure only the main site header block (typically wrapping the logo, search, and navigation) carries the <header role="banner"> or top-level <header> tag.
- Any secondary <header> elements in widgets, CMS blocks, or product listing blocks should be changed to <div> or wrapped inside an <article> or <section>.
- Run bin/magento cache:flush and bin/magento setup:static-content:deploy after saving changes.
- Validate with the axe DevTools browser extension on the live or staging store.
Landmark no duplicate contentinfoModerate effort
Remove duplicate `<footer>` elements or `role="contentinfo"` landmarks so your page has exactly one, site-wide footer region.
On Adobe Commerce (Magento)
- In your theme directory (e.g. `app/design/frontend/<Vendor>/<theme>/`), open `Magento_Theme/templates/html/footer.phtml` — this is the main footer template.
- Search this file and all layout XML files under `Magento_Theme/layout/` for `<footer` and `role="contentinfo"`. The tag should appear only once in the rendered page.
- Also check your custom theme's `default.xml` or `cms_index_index.xml` layout files to make sure you have not added a second footer container block that renders its own `<footer>` tag.
- If a third-party module injects an additional footer, locate its template in `app/code/<Vendor>/<Module>/view/frontend/templates/` and change the wrapping `<footer>` tag to a `<div>`.
- Clear cache: go to System → Cache Management → Flush Magento Cache, then re-test with axe DevTools.
- Always make changes in a custom child theme, never in core or vendor files.
Landmark no duplicate mainModerate effort
Ensure each page contains exactly one `<main>` landmark element (or one element with `role="main"`) so screen-reader users can navigate directly to the page's primary content.
On Adobe Commerce (Magento)
- The main landmark is defined in your theme's layout XML and PHTML templates. Locate your active theme under 'app/design/frontend/<Vendor>/<Theme>/'.
- Open '1column.phtml', '2columns-left.phtml', etc. in the 'Magento_Theme/templates/root/' directory and search for '<main' and 'role="main"'. There should be only one.
- Also search layout XML files (e.g. 'default.xml', 'catalog_product_view.xml') for any '<referenceContainer>' or '<block>' that adds a second 'role="main"' attribute via a template.
- If a third-party extension is adding the duplicate, override its template in your custom theme by copying the PHTML to your theme path and editing it to remove or change the '<main>' tag.
- Run 'php bin/magento cache:clean' and 'setup:static-content:deploy', then re-test with the axe DevTools extension.
Landmark one mainQuick win
Add a single `<main>` landmark element (or `role="main"`) to every page so that screen-reader users and assistive technologies can skip directly to the primary content.
On Adobe Commerce (Magento)
- In your custom or child theme, locate `Magento_Theme/templates/root.phtml` (Magento 2) or `app/design/frontend/<Vendor>/<theme>/Magento_Theme/templates/root.phtml`.
- Find the main content wrapper — typically `<div id="maincontent" class="page-main">` — and change it to `<main id="main-content" role="main" class="page-main">` (keep existing classes for CSS compatibility).
- Also update the matching closing tag to `</main>`.
- In the same file, add a skip link just after `<body>`: `<a class="action skip" href="#main-content"><span>Skip to main content</span></a>` (Magento's blank theme includes CSS for `.action.skip` to be visually hidden until focused).
- Run `bin/magento cache:clean` and `bin/magento setup:static-content:deploy`, then verify with the axe extension.
Landmark uniqueModerate effort
Add a unique aria-label (or aria-labelledby) to every repeated landmark role so assistive technologies can distinguish between them.
On Adobe Commerce (Magento)
- Via SSH or SFTP, navigate to your active theme directory: app/design/frontend/<Vendor>/<theme>/.
- Open Magento_Theme/templates/html/header.phtml and locate the primary <nav> element (often wrapping the navigation block).
- Add aria-label="Primary navigation" to that element. For breadcrumbs, open Magento_Theme/templates/html/breadcrumbs.phtml and add aria-label="Breadcrumbs" to the wrapping <nav>.
- For the footer navigation, open Magento_Theme/templates/html/footer.phtml and label its <nav> with aria-label="Footer navigation".
- Run bin/magento cache:flush after saving changes, then verify on the storefront using browser DevTools and your accessibility scanner.
Link in text blockModerate effort
Make inline links visually distinguishable from surrounding body text by ensuring at least 3:1 color contrast between the link color and the non-link text color, or by adding a non-color visual cue (such as underline) to every link.
On Adobe Commerce (Magento)
- Create or edit a child theme to avoid losing changes on upgrade: `app/design/frontend/<Vendor>/<theme>/web/css/source/_theme.less` (or `_extend.less`).
- Locate or add the link color variable — in Luma-based themes look for `@link__color` in `web/css/source/_variables.less` — and set it to a hex value passing ≥3:1 contrast against body text.
- To enforce underlines globally, add: `a { text-decoration: underline; }` in your `_extend.less` or equivalent SCSS partial.
- Run `bin/magento setup:static-content:deploy` and clear cache (`bin/magento cache:flush`) to compile the updated CSS.
- Verify on a CMS page, product description, and blog post (if using a blog extension) using browser DevTools accessibility panel.
Link nameModerate effort
Add a descriptive, screen-reader-accessible label to every link on your store so assistive technologies can announce where each link leads.
On Adobe Commerce (Magento)
- SSH into your server (or use your IDE) and locate the PHTML or HTML template file responsible for the problematic link — common paths: app/design/frontend/<Vendor>/<theme>/Magento_Theme/templates/html/header.phtml (for header icons), Magento_Catalog/templates/product/list.phtml (for product card links), Magento_Theme/templates/html/footer.phtml (for footer/social links).
- Add aria-label='<descriptive text>' to the <a> tag, or for linked images ensure the <img> tag has a non-empty alt attribute.
- If using a third-party theme, create a template override in your custom theme directory rather than editing vendor files directly.
- Run bin/magento cache:clean && bin/magento cache:flush after saving.
- Re-test with the axe DevTools browser extension on the front end.
ListModerate effort
Fix all HTML list elements so that `<ul>` and `<ol>` contain only valid `<li>` children, and `<li>` elements appear only inside a proper list container, ensuring correct semantic list structure throughout your store.
On Adobe Commerce (Magento)
- Identify which template (.phtml) or layout XML file generates the broken list. Navigation is commonly in 'Magento_Theme/templates/html/header.phtml' or a custom module; product attributes may be in 'Magento_Catalog/templates/product/view/attributes.phtml'.
- To safely override a core template, copy it into your custom theme directory at 'app/design/frontend/<Vendor>/<theme>/<Module>/templates/...' mirroring the original path.
- Open the copied template and search for '<ul', '<ol', or '<li'. Fix any invalid nesting: wrap non-<li> direct children inside <li> tags, and ensure all <li> elements are inside a <ul>, <ol>, or <menu>.
- Run 'bin/magento cache:flush' and 'bin/magento setup:static-content:deploy' (if in production mode) to apply the changes.
- Verify by loading the affected page and running axe DevTools or WAVE in the browser.
ListitemQuick win
Wrap every `<li>` element in a proper `<ul>` or `<ol>` parent so screen readers and browsers can correctly identify and announce list structure.
On Adobe Commerce (Magento)
- Identify the offending template file using the Layout/Template hints tool: Stores → Configuration → Advanced → Developer → Debug → enable Template Path Hints for Storefront.
- Navigate to the flagged page on the frontend; the hint overlay will show the exact .phtml file responsible (e.g. 'Magento_Theme/templates/html/header.phtml' or a custom module template).
- Edit that .phtml file in your custom theme directory (app/design/frontend/Vendor/theme/), locating the orphaned `<li>` and wrapping it in `<ul>…</ul>`.
- If using Page Builder (Magento 2.4+), go to Content → Pages or Blocks, open the item, switch to the HTML Code content type, and correct the markup there.
- Run 'bin/magento cache:clean' after saving the file, then reload the page.
- Verify with the axe browser extension and disable Template Path Hints when done.
Meta refreshQuick win
Remove or disable any `<meta http-equiv="refresh">` tag that automatically redirects or reloads the page in under 20 hours.
On Adobe Commerce (Magento)
- Connect to your server via SSH or SFTP and search your active theme directory (app/design/frontend/<Vendor>/<theme>/) for the string 'http-equiv' using: `grep -r 'http-equiv' app/design/frontend/`
- Also search the base Magento layout files: `grep -r 'http-equiv' vendor/magento/` to confirm whether the tag originates from core, a module, or your theme.
- If found in a theme file such as 'Magento_Theme/layout/default_head_blocks.xml' or a '.phtml' template, edit that file to remove the `<meta http-equiv="refresh" ...>` block.
- If a third-party extension is responsible, identify it from the grep results and either configure it to disable the meta-refresh or remove the extension.
- For URL redirects, use Admin → Marketing → SEO & Search → URL Rewrites to create a proper 301 redirect instead.
- After making changes, flush the Magento cache: Admin → System → Cache Management → Flush Magento Cache, then verify the page no longer auto-refreshes.
Meta viewportQuick win
Remove `user-scalable=no` (and any `maximum-scale` value below 5) from your site's `<meta name="viewport">` tag so visitors can pinch-to-zoom on mobile devices.
On Adobe Commerce (Magento)
- Via SSH or FTP, navigate to your active theme directory: `app/design/frontend/<Vendor>/<theme>/Magento_Theme/layout/`.
- Open `default_head_blocks.xml` (or `default.xml`).
- Search for the viewport meta tag. Alternatively, check `app/design/frontend/<Vendor>/<theme>/Magento_Theme/templates/root.phtml`.
- Remove `user-scalable=no` and fix any `maximum-scale` value below 5 in the content attribute.
- If editing the base Luma theme, copy the file to your custom child theme first to preserve changes across upgrades.
- Run `bin/magento cache:flush` and test on mobile.
Meta viewport largeQuick win
Remove or raise the `maximum-scale` value in your site's `<meta name="viewport">` tag so mobile users can pinch-to-zoom freely.
On Adobe Commerce (Magento)
- Via SFTP or your server file manager, navigate to your active theme folder: app/design/frontend/<Vendor>/<Theme>/Magento_Theme/layout/.
- Open (or create) default_head_blocks.xml.
- Locate any `<meta name="viewport"` node and update its content attribute to remove `maximum-scale` restrictions.
- If the tag is set in a .phtml file, check Magento_Theme/templates/html/head.phtml in your theme.
- Run `bin/magento cache:flush` and `bin/magento setup:static-content:deploy` after saving.
- Verify on mobile that pinch-to-zoom is now permitted.
Nested interactiveModerate effort
Remove or restructure focusable elements nested inside interactive controls so that no interactive element contains another focusable child.
On Adobe Commerce (Magento)
- Identify the template file rendering the offending component. Product cards are typically in app/design/frontend/<Vendor>/<Theme>/Magento_Catalog/templates/product/list.phtml or a widget template.
- Copy the relevant .phtml file into your custom theme directory (following Magento's template override hierarchy) if you have not already.
- Open the file and locate the wrapping <a> element (usually wrapping the product image and name) that also contains a button or another link (e.g. Add to Cart, Wishlist).
- Refactor: change the outer wrapper to a <div>, give the product name/image its own <a href>, and ensure all action buttons (Add to Cart, Compare, Wishlist) are sibling elements of that link, not children.
- Run bin/magento cache:clean and bin/magento setup:static-content:deploy (if in production mode) to clear compiled templates.
- Test keyboard navigation on the category page in a browser.
Object altQuick win
Add a descriptive text alternative to every `<object>` element so screen readers can convey its content to users who cannot see it.
On Adobe Commerce (Magento)
- Connect to your server and locate the relevant PHTML template file (e.g. under `app/design/frontend/<Vendor>/<Theme>/Magento_Catalog/templates/` or a custom module's `view/frontend/templates/`).
- Find the `<object>` tag and add `aria-label="Descriptive text here"` to the opening tag; also add meaningful fallback text or a link between the open and close tags.
- If the object is placed via a CMS page or block: in the admin, go to Content → Pages (or Blocks) → Edit the relevant item → click 'Show/Hide Editor' to reveal the raw HTML, then edit the `<object>` tag.
- Clear the Magento cache: System → Cache Management → Flush Magento Cache (or run `bin/magento cache:flush` via CLI).
- Verify on the front end using the axe DevTools browser extension.
Page has heading oneModerate effort
Add a single, descriptive `<h1>` heading to every page so screen readers and search engines can identify the page's main topic.
On Adobe Commerce (Magento)
- **Product pages:** In your theme, locate `app/design/frontend/<Vendor>/<theme>/Magento_Catalog/templates/product/view/overview.phtml` (or the relevant `.phtml` for the title). Confirm the product name is rendered inside `<h1 class="page-title">…</h1>`. The default Luma theme already does this — if yours does not, override the template in your custom theme.
- **Category pages:** Check `app/design/frontend/<Vendor>/<theme>/Magento_Catalog/templates/category/view.phtml` and ensure `$block->getLayer()->getCurrentCategory()->getName()` is wrapped in `<h1>`.
- **CMS pages:** Go to Content → Pages → edit the page → expand 'Content' → switch to HTML editor and add `<h1>Your Page Title</h1>` as the first heading.
- **Layout XML override:** Alternatively, use `catalog_product_view.xml` / `catalog_category_view.xml` layout XML files to inject or correct the heading block without touching `.phtml` files.
- Flush cache (System → Cache Management → Flush Magento Cache) and verify on the frontend.
Presentation role conflictModerate effort
Remove conflicting ARIA attributes and tabindex from elements that are marked as presentational (role="presentation" or role="none"), so screen readers consistently ignore them.
On Adobe Commerce (Magento)
- Identify the conflicting element via browser DevTools and note which template file (*.phtml) or layout XML file is responsible (use template hints: Stores → Configuration → Advanced → Developer → Debug → Enable Template Path Hints).
- In your custom theme directory (app/design/frontend/<Vendor>/<theme>/), locate the relevant .phtml file.
- Open the file and find the element with role="presentation" or role="none" that also carries aria-* attributes or tabindex.
- Edit the element to remove the conflicting attribute(s) per the generic steps, then save.
- From the Magento CLI run: php bin/magento cache:flush and php bin/magento setup:static-content:deploy.
- Reload the storefront page and verify with axe DevTools that the conflict is resolved.
RegionModerate effort
Wrap all visible page content inside HTML landmark elements (such as `<main>`, `<nav>`, `<header>`, `<footer>`, or ARIA `role` attributes) so screen-reader users can navigate your store efficiently.
On Adobe Commerce (Magento)
- Landmark structure lives in your active theme's layout XML and PHTML templates. Navigate to `app/design/frontend/[Vendor]/[Theme]/Magento_Theme/templates/` (or the equivalent in `vendor/magento/` if you haven't overridden it).
- Open `html/header.phtml` and ensure the outermost element is `<header role="banner">` (or add the `role` attribute if the tag is a `<div>`).
- Open `html/footer.phtml` and ensure it uses `<footer role="contentinfo">`.
- In your theme's `default.xml` layout file (`Magento_Theme/layout/default.xml`), confirm the main content container block renders inside a `<main>` tag. If not, override the `1column.phtml` / `2columns-left.phtml` templates under `Magento_Theme/templates/html/` and wrap the main column in `<main id="maincontent" role="main">`.
- For CMS blocks or widgets that render outside these landmarks (e.g. a full-width promo strip injected above the header block), edit the CMS block HTML in Content → Blocks → [Block Name] and wrap the content in `<section role="region" aria-label="[Description]">...</section>`.
- Run `bin/magento cache:clean`, then use axe DevTools on the frontend to verify all violations are resolved.
Role img altQuick win
Add an accessible text label (aria-label) to every element that has role="img" so screen readers can announce what the image conveys.
On Adobe Commerce (Magento)
- Locate the relevant PHTML template or layout XML in your custom theme under app/design/frontend/<Vendor>/<Theme>/.
- Search across templates for role="img" using grep -r 'role="img"' app/design/ in the CLI.
- In each flagged template, add aria-label="<?= $block->escapeHtmlAttr(__('Descriptive text')) ?>" to the element, using Magento's translation/escaping helpers.
- For SVGs included via the Magento SVG icon system, edit the relevant .phtml partial in Magento_Theme or your override and add the aria-label there.
- Run bin/magento cache:flush after saving, then test on the storefront with axe DevTools.
Scrollable region focusableQuick win
Make every scrollable region on your store reachable and operable by keyboard by adding tabindex="0" (or placing focusable content inside it) so users who cannot use a mouse can scroll it.
On Adobe Commerce (Magento)
- In your theme directory (app/design/frontend/<Vendor>/<theme>/), locate the relevant .phtml template — commonly Magento_Catalog/templates/product/view/description.phtml.
- Find the container div that wraps the scrollable content and add tabindex="0" aria-label="Product description" to its opening tag.
- Add a focus CSS rule in your theme's web/css/source/ Less/CSS file: .product.description:focus { outline: 2px solid #005fcc; outline-offset: 2px; }
- Run bin/magento setup:static-content:deploy and bin/magento cache:clean to apply changes.
- Verify in a browser using keyboard Tab navigation.
Select nameModerate effort
Add a descriptive, programmatically associated label to every `<select>` dropdown element on the site so assistive technologies can announce its purpose to users.
On Adobe Commerce (Magento)
- Identify the PHTML template responsible for the <select>. Common locations: 'app/design/frontend/VENDOR/THEME/Magento_Catalog/templates/product/view/options/' (product options), 'app/design/frontend/VENDOR/THEME/Magento_Checkout/templates/onepage/' (checkout country/state), 'app/design/frontend/VENDOR/THEME/Magento_LayeredNavigation/' (filter dropdowns).
- Copy the core template into your custom theme directory to avoid losing changes on upgrade, then open the file.
- Locate the <select> tag and add a <label> element with a matching 'for' attribute, or add aria-label="..." directly on the <select> tag.
- Run 'bin/magento cache:clean' and 'bin/magento setup:static-content:deploy' (production mode) after saving.
- Verify the fix with axe DevTools and NVDA/VoiceOver.
Skip linkQuick win
Add a valid, matching target ID to every skip-navigation link so keyboard and assistive-technology users can bypass repeated header content and jump directly to the main content area.
On Adobe Commerce (Magento)
- The skip link is defined in the base template. Locate app/design/frontend/<Vendor>/<Theme>/Magento_Theme/templates/root.phtml (or the Luma/Blank theme equivalent at vendor/magento/theme-frontend-luma/templates/root.phtml — copy to your custom theme first).
- Near the top of the <body> output, add: `<a class='action skip contentarea' href='#contentarea'><span><?= $escaper->escapeHtml(__('Skip to Content')) ?></span></a>`
- Find the main content wrapper in app/design/frontend/<Vendor>/<Theme>/Magento_Theme/layout/default.xml or in the relevant .phtml template (typically the <main> or <div class='page-main'>) and ensure it has `id='contentarea'` and `tabindex='-1'`.
- Run `bin/magento setup:static-content:deploy` and clear the full-page cache (`bin/magento cache:flush`) after making changes.
- Test on the frontend with keyboard navigation.
Svg img altModerate effort
Add a meaningful text alternative to every SVG image so screen readers can describe it to visually impaired shoppers.
On Adobe Commerce (Magento)
- SVG icons and logos are typically stored in your theme at `app/design/frontend/<Vendor>/<Theme>/web/images/` or within `.phtml` template files.
- Locate the template rendering the SVG (e.g. `Magento_Theme/templates/html/header.phtml` for the logo).
- Edit the `<svg>` tag in the template to add `role='img'` and `aria-labelledby='UNIQUE-ID'`; insert `<title id='UNIQUE-ID'>Descriptive text</title>` as the first child element.
- For SVG sprites or icon fonts managed in `Magento_Ui`, edit the relevant UI component template or override it in your custom theme.
- For decorative SVGs, add `aria-hidden='true'` and `focusable='false'` to the `<svg>` tag.
- Run `bin/magento cache:flush` after changes, then test in a browser with the axe extension or a screen reader.
TabindexModerate effort
Remove all positive tabindex values (tabindex="1" or higher) from your store's HTML elements, replacing them with tabindex="0" or relying on natural document order to control keyboard focus.
On Adobe Commerce (Magento)
- Identify the .phtml template or .html Knockout/UI-component template responsible for the element. Use your IDE's global project search for 'tabindex' across app/design/ and vendor/ (prefer overriding in app/design/frontend/<Vendor>/<theme>/).
- Create a theme override in your custom theme directory mirroring the original file path, then remove or correct the positive tabindex value in the override file.
- For JavaScript-rendered components (e.g. minicart, checkout), search the corresponding .js or Knockout .html binding template for tabindex and apply the same correction.
- Run `bin/magento cache:flush` and `bin/magento setup:static-content:deploy` to apply the changes.
- Use a keyboard to tab through the affected storefront page (especially checkout) to confirm logical, sequential focus order.
Valid langModerate effort
Add a valid BCP 47 language code to every `lang` attribute on your pages so assistive technologies can read content in the correct language.
On Adobe Commerce (Magento)
- The `<html lang>` tag is rendered in the root page template. In the default Luma/Blank theme, open `vendor/magento/module-theme/view/base/templates/root.phtml` — but do NOT edit core files; instead, copy it to your custom theme: `app/design/frontend/[Vendor]/[theme]/Magento_Theme/templates/root.phtml`.
- In that file, find the `<html` tag. Replace any hard-coded or invalid `lang` attribute with the dynamic value: `lang="<?= $block->escapeHtmlAttr($block->getLocaleCode()) ?>"` or use the store's locale helper to fetch the correct BCP 47 code.
- Set the store locale in Admin → Stores → Configuration → General → General → Locale Options → Locale. Choose the correct locale; Magento maps it to a BCP 47 code.
- For content blocks (CMS Blocks/Pages) that contain inline `lang` attributes in their HTML, edit them in Admin → Content → Blocks (or Pages), switch to HTML view, and correct the `lang` values.
- Run `bin/magento cache:flush` after changes, then verify with the axe extension.
Security (OWASP) · 23 fixes
Dmarc policy noneModerate effort
Strengthen your DMARC policy from p=none (monitor-only) to p=quarantine, then p=reject, to actively block email spoofing of your domain.
On Adobe Commerce (Magento)
- Adobe Commerce is self-hosted or cloud-hosted; DNS is controlled at your registrar, hosting provider, or cloud DNS service (e.g., AWS Route 53, Cloudflare).
- Log in to your DNS management interface and locate the _dmarc TXT record for your domain.
- Edit the record: change p=none to p=quarantine. Save and allow up to 48 hours for propagation.
- Adobe Commerce sends transactional emails via your configured SMTP (set in Stores > Configuration > Advanced > System > Mail Sending Settings). Confirm the sending domain matches your From address domain, and that SPF and DKIM are configured at your SMTP relay provider (SendGrid, Mailgun, etc.).
- Review DMARC aggregate reports to confirm alignment, then update the _dmarc record to p=reject.
Hsts disabledQuick win
Enable HTTP Strict-Transport-Security (HSTS) by setting a max-age of at least 31536000 seconds (one year) so browsers always use HTTPS when visiting your store.
On Adobe Commerce (Magento)
- In Adobe Commerce Admin, go to Stores → Configuration → General → Web → Base URLs (Secure) and ensure all base URLs use https://. Set 'Use Secure URLs on Storefront' and 'Use Secure URLs in Admin' both to Yes.
- Add the HSTS header at the web server level — this is the most reliable method for Magento/Adobe Commerce deployments.
- Nginx: In your Magento nginx.conf or the server block for your domain (typically /etc/nginx/sites-available/magento.conf), inside the `server { listen 443; }` block add: `add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;` then reload Nginx.
- Apache: In your VirtualHost block for port 443 in your .conf file or in pub/.htaccess add: `Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"` and restart Apache.
- If hosted on Adobe Commerce Cloud (Magento Cloud), add the header in your .magento.app.yaml under web.locations headers or use a Fastly VCL snippet in the Fastly configuration panel within the Admin under Stores → Configuration → Advanced → System → Full Page Cache → Fastly Configuration.
Hsts max age too shortQuick win
Increase your HSTS max-age to at least 31536000 (one year) so browsers enforce HTTPS-only connections for a meaningful period.
On Adobe Commerce (Magento)
- Log in to the Admin panel → Stores → Configuration → General → Web → Base URLs (Secure). Confirm 'Use Secure URLs on Storefront' and 'Use Secure URLs in Admin' are both set to Yes.
- To set the HSTS header, edit your web-server configuration directly (Adobe Commerce is self-hosted or on Adobe Commerce Cloud).
- For Apache: Open <VirtualHost *:443> in your site config (e.g. /etc/apache2/sites-available/magento.conf) and add inside it: Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" — then run: sudo systemctl reload apache2
- For Nginx: In your server { listen 443 ssl; } block in /etc/nginx/sites-available/magento.conf add: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; — then run: sudo systemctl reload nginx
- On Adobe Commerce Cloud (Fastly CDN): In Admin → Stores → Configuration → Advanced → System → Full Page Cache → Fastly Configuration, use the Custom VCL or Response Headers section to add the HSTS header, or configure it in the Fastly dashboard under Headers.
- Verify with DevTools or securityheaders.com.
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 Adobe Commerce (Magento)
- Obtain and install an SSL certificate on your web server (Apache or Nginx) via your hosting provider or Let's Encrypt (e.g. using Certbot: sudo certbot --apache -d yourdomain.com).
- In your Magento Admin, go to Stores → Configuration → General → Web.
- Expand the 'Base URLs (Secure)' section and set 'Secure Base URL' to https://yourdomain.com/.
- Set 'Use Secure URLs on Storefront' and 'Use Secure URLs in Admin' both to 'Yes'.
- Expand the 'URL Options' section and ensure 'Auto-redirect to Base URL' is set to 'Yes (302 Found)' or 'Yes (301 Moved Permanently)' to redirect HTTP to HTTPS.
- Clear the Magento cache: System → Cache Management → Flush Magento Cache. Also flush your full-page cache (Varnish if applicable).
- Update your web server configuration (Apache .htaccess or Nginx server block) to add a server-level 301 redirect from HTTP to HTTPS as an additional safeguard.
Info disclosure serverQuick win
Remove or obscure the Server HTTP response header so your web server software name and version are no longer exposed to the public internet.
On Adobe Commerce (Magento)
- Adobe Commerce is typically self-hosted or hosted on Adobe Commerce Cloud — you (or your DevOps team) control the server stack.
- For nginx (most common): SSH into the server, edit `/etc/nginx/nginx.conf` or the Magento vhost config, add `server_tokens off;` inside the `http {}` or `server {}` block, then reload nginx: `sudo nginx -s reload`.
- For Apache: edit the main `httpd.conf` or the vhost `.conf` file — add `ServerTokens Prod` and `ServerSignature Off`, then restart Apache: `sudo systemctl restart httpd`.
- For Adobe Commerce Cloud (cloud.magento.com): add the header suppression in `.magento/routes.yaml` or via a Fastly custom VCL snippet in the Magento Admin → Stores → Configuration → Advanced → System → Full Page Cache → Fastly Configuration → Custom VCL Snippets, adding a `recv` or `deliver` snippet to unset `beresp.http.Server`.
- Verify: `curl -I https://yourstore.com` — the `Server` header should be absent or show only a generic value with no version number.
Info disclosure x powered byQuick win
Remove or mask the X-Powered-By HTTP response header to stop advertising your server technology stack to attackers.
On Adobe Commerce (Magento)
- For Nginx: Edit your Magento Nginx server block config (typically at /etc/nginx/sites-available/magento or your server's vhost file). Add `more_clear_headers 'X-Powered-By';` inside the `server {}` block, then run `nginx -t && systemctl reload nginx`.
- For Apache: Open or create a .htaccess in your Magento web root. Add `Header unset X-Powered-By` (mod_headers must be enabled). Alternatively, set it in your VirtualHost config.
- For PHP-FPM: Edit php.ini (or a pool .conf file) and set `expose_php = Off` — this prevents PHP from adding its own X-Powered-By value.
- For Adobe Commerce Cloud (cloud.adobe.com): Add a custom Fastly VCL snippet via Admin → Stores → Configuration → Advanced → System → Full-Page Cache → Fastly Configuration → Custom VCL Snippets, with a `vcl_deliver` subroutine that calls `unset resp.http.X-Powered-By;`.
- Verify with: `curl -I https://yourstore.com | grep -i powered`
Insecure cookieModerate effort
Set the HttpOnly, Secure, and SameSite=Strict flags on every session and CSRF cookie your store sets so they cannot be stolen by malicious scripts or sent over unencrypted connections.
On Adobe Commerce (Magento)
- Log in to the Admin Panel and navigate to Stores → Configuration → General → Web → Session Cookie Management.
- Set 'Cookie Lifetime' appropriately, then locate and enable 'Cookie Restriction Mode'; ensure 'Use Secure Cookie' is set to 'Yes' and 'HTTP Only' is set to 'Yes'.
- For SameSite: in Magento 2.3.5+, go to Stores → Configuration → General → Web → Default Cookie Settings and set 'Cookie SameSite' to 'Strict'.
- If those UI options are unavailable (older versions), add to app/etc/env.php under 'session' config: `'cookie_secure' => 1, 'cookie_httponly' => 1, 'cookie_samesite' => 'Strict'`.
- Deploy config changes: run `bin/magento config:set web/cookie/cookie_httponly 1`, `bin/magento config:set web/cookie/cookie_secure 1`, then `bin/magento cache:flush` via SSH.
- Verify in browser DevTools → Application → Cookies that PHPSESSID and form_key cookies all carry the correct flags.
Missing content security policyModerate effort
Add a Content-Security-Policy (CSP) response header to every page so browsers block unauthorized scripts, styles, and resources from loading.
On Adobe Commerce (Magento)
- Adobe Commerce (and Magento Open Source) has built-in CSP support since version 2.3.5. Go to Admin Panel → Stores → Configuration → Security → Content Security Policy.
- Set 'Enable CSP in Report Only Mode' to 'Yes' first to collect violations without breaking the site, then configure your policy directives in the same panel.
- Alternatively, edit your theme's `csp_whitelist.xml` file (located at `app/design/frontend/[Vendor]/[Theme]/etc/csp_whitelist.xml`) to whitelist specific external domains per directive.
- For server-level enforcement (recommended in addition to application-level), add `Header always set Content-Security-Policy "..."` to your Apache `.htaccess` or virtual host config, or `add_header Content-Security-Policy "..."` to your Nginx `server {}` block.
- Run `bin/magento cache:flush` after any configuration changes, then verify with browser DevTools → Network → document response headers.
Missing dmarcQuick win
Add a DMARC DNS TXT record at _dmarc.yourdomain.com to protect your domain from email spoofing and phishing.
On Adobe Commerce (Magento)
- Adobe Commerce / Magento is self-hosted — DNS is managed at your domain registrar or DNS provider (e.g. Cloudflare, Route 53, GoDaddy), not inside the Magento admin.
- Log into your DNS provider's control panel and add a TXT record: Name/Host = '_dmarc', TTL = 3600, Value = v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com
- In the Magento admin, go to Stores → Configuration → Advanced → System → Mail Sending Settings. Ensure you are using an authenticated SMTP relay (e.g. SendGrid, Mailgun, Amazon SES) rather than the server's default sendmail — this is critical for SPF/DKIM alignment.
- Install an SMTP extension (e.g. Magento SMTP by Mageplaza, or configure via app/etc/config.php) to route all transactional mail through your authenticated sending provider.
- Confirm SPF and DKIM records for your SMTP provider are already in DNS for your domain, then monitor DMARC reports for 2–4 weeks before escalating to p=quarantine.
Missing permissions policyQuick win
Add a Permissions-Policy HTTP response header to explicitly restrict which browser features (camera, microphone, geolocation, etc.) your store's pages are allowed to use.
On Adobe Commerce (Magento)
- For Apache-based deployments: edit your .htaccess file in the Magento root directory or your virtual host config and add: `Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()"` — ensure mod_headers is enabled in Apache.
- For Nginx-based deployments: add to your nginx.conf server {} block: `add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()" always;` then run `nginx -t` to validate and `systemctl reload nginx`.
- Alternatively, add the header programmatically via a custom Magento module: create a plugin on Magento\Framework\App\Response\Http that calls $response->setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()', true); in an afterSendResponse method.
- If using Adobe Commerce Cloud, add the header in your .magento.app.yaml or via Fastly (the bundled CDN): Stores → Configuration → Advanced → System → Full Page Cache → Fastly Configuration → Custom VCL Snippets, or use Fastly's response header rules.
- Flush the full-page cache after any change: System → Cache Management → Flush Magento Cache.
- Verify with Chrome DevTools → Network → Response Headers.
Missing referrer policyQuick win
Add a `Referrer-Policy: strict-origin-when-cross-origin` HTTP response header to every page so browsers control what referrer information is sent with requests.
On Adobe Commerce (Magento)
- For Nginx-hosted stores, open your virtual host config (e.g. `/etc/nginx/sites-available/magento.conf`) and add inside the `server {}` block: `add_header Referrer-Policy "strict-origin-when-cross-origin" always;` — then run `nginx -t && nginx -s reload`.
- For Apache-hosted stores, open `pub/.htaccess` or your VirtualHost config and add: `Header always set Referrer-Policy "strict-origin-when-cross-origin"`.
- Alternatively, add the header programmatically: create a custom module or edit `app/code/YourVendor/YourModule/Plugin/ResponsePlugin.php` to use `$response->setHeader('Referrer-Policy', 'strict-origin-when-cross-origin', true)` via a plugin on `Magento\Framework\App\Response\HttpInterface`.
- For Adobe Commerce Cloud (cloud.magento.com): add the header in `.magento.app.yaml` under the `web.headers` section or use Fastly (the built-in CDN) → Fastly Configuration → Response Object → add the header.
- Run `php bin/magento cache:flush` after any config change and verify with browser DevTools.
Missing spfQuick win
Add a DNS TXT record containing a valid SPF policy to your domain so email servers can verify that messages sent from your domain are legitimate.
On Adobe Commerce (Magento)
- Adobe Commerce (cloud or on-premises) does not manage your DNS — edit records at your registrar or DNS provider (Cloudflare, Route 53, etc.).
- Log in to your DNS provider and add a TXT record: Name = @, TTL = 3600, Value = your SPF string.
- Adobe Commerce Cloud uses SendGrid for transactional email by default; add 'include:sendgrid.net' to your SPF record. Confirm the current include tag in Adobe Commerce Cloud's official documentation.
- For on-premises installations using a custom SMTP relay (configured in Admin → Stores → Configuration → Advanced → System → Mail Sending Settings), add the SPF include for whichever SMTP service you are using.
- If you use a third-party email extension (e.g. from Adobe Commerce Marketplace), check that extension's documentation for the correct SPF include directive.
- Verify propagation and record correctness with a free SPF lookup tool.
Missing strict transport securityQuick win
Add an HTTP Strict-Transport-Security (HSTS) response header with at least `max-age=31536000; includeSubDomains` to every HTTPS response your store sends.
On Adobe Commerce (Magento)
- Open your Nginx or Apache virtual host config for the store.
- Nginx: In the SSL server block add: `add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;` — then run `nginx -t && systemctl reload nginx`.
- Apache: In the VirtualHost block for port 443 add: `Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"` — then run `apachectl configtest && systemctl reload apache2`.
- If you use a CDN (Fastly, Cloudflare, Varnish), configure the header there so it is sent on every edge response.
- Adobe Commerce Cloud (cloud.magento.com): Update your `.magento.app.yaml` or configure Fastly via the Magento Admin → Stores → Configuration → Advanced → System → Full Page Cache → Fastly Configuration → Custom VCL snippets to inject the header.
- Flush the Magento full-page cache after any config change: Admin → System → Cache Management → Flush Cache Storage.
- Verify with DevTools or `curl -I https://yourstore.com` and look for the `strict-transport-security` header.
Missing x content type optionsQuick win
Add the `X-Content-Type-Options: nosniff` HTTP response header to every page of your store so browsers never guess at file types.
On Adobe Commerce (Magento)
- In Adobe Commerce / Magento 2, go to Admin → Stores → Configuration → General → Web → Default Headers section — note that full custom security header control is typically done at the server/Nginx level.
- For Nginx (most common): open your Magento Nginx config file (usually at /etc/nginx/sites-available/your-store.conf or the Magento-provided nginx.conf.sample) and inside the server {} block add: add_header X-Content-Type-Options nosniff always;
- For Apache: add 'Header always set X-Content-Type-Options "nosniff"' to your .htaccess file in the Magento root, or to your VirtualHost block.
- Alternatively, create a custom Magento plugin/interceptor on \Magento\Framework\App\Response\Http to programmatically set the header on all responses via a before/after plugin on the sendResponse() method.
- Clear Magento's full-page cache (Admin → System → Cache Management → Flush Magento Cache) then verify in browser dev-tools.
Missing x frame optionsQuick win
Add an X-Frame-Options HTTP response header set to DENY or SAMEORIGIN to prevent your store's pages from being embedded in iframes on other websites.
On Adobe Commerce (Magento)
- In the Magento Admin Panel, go to Stores → Configuration → General → Web → Default Pages — check if a built-in X-Frame-Options setting exists (Magento 2.x exposes this under Stores → Configuration → Security).
- For Magento 2.x: go to Stores → Configuration → Security → set 'X-Frame-Options Header' to SAMEORIGIN and save.
- Alternatively, open your Nginx server block config (typically /etc/nginx/sites-available/your-store.conf) and add inside the server{} block: add_header X-Frame-Options SAMEORIGIN always; Then reload Nginx: sudo nginx -s reload
- For Apache: open your .htaccess or VirtualHost config and add: Header always set X-Frame-Options SAMEORIGIN Then restart Apache.
- Flush the Magento full-page cache: System → Cache Management → Flush Cache Storage.
- Verify via DevTools → Network → Response Headers.
Passive scan onlyModerate effort
Complement passive security scans with active Dynamic Application Security Testing (DAST) against a staging copy of your store before each release.
On Adobe Commerce (Magento)
- Adobe Commerce (self-hosted or cloud) gives you full server access, making it the most straightforward platform for DAST. Set up a staging environment that mirrors production — same Magento version, same extensions, same configuration.
- Restrict staging access: configure your web server (Nginx/Apache) or CDN to whitelist only your IP for DAST traffic, and disable indexing via robots.txt on staging.
- Install OWASP ZAP on a local or CI machine. Set the target to your staging URL (e.g. https://staging.yourstore.com).
- Configure authenticated scanning: record a ZAP login script using a Magento test-customer account to allow ZAP to scan cart, checkout, customer account, and any custom REST/GraphQL API endpoints under /rest/ and /graphql.
- Run the Active Scan. Magento's common risk areas include custom modules, third-party extensions, and the Admin Panel URL — ensure the Admin is not on the default /admin path and is IP-restricted.
- Integrate ZAP CLI into your CI/CD pipeline (e.g. GitHub Actions, GitLab CI) so a DAST scan runs automatically against staging on every release branch before merge to production.
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 Adobe Commerce (Magento)
- Adobe Commerce does not manage SSL — it is handled at the server/hosting level (cloud hosting, on-premise, or managed hosting partner).
- For Adobe Commerce Cloud: the certificate is managed by Fastly CDN. Log in to the Cloud Console (cloud.magento.com), navigate to your environment, go to Services → Fastly. SSL certificates are auto-renewed; verify the expiry date in the Fastly dashboard.
- For on-premise or third-party hosting: log in to your server or control panel, locate the SSL/TLS manager (e.g. cPanel → SSL/TLS), and renew or replace the certificate.
- After installing a new certificate, update the Magento base URLs if needed: Admin Panel → Stores → Configuration → General → Web → Base URLs (Secure) — ensure all use https://.
- Run 'bin/magento cache:flush' from the server command line after any configuration change to clear the config cache.
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 Adobe Commerce (Magento)
- Adobe Commerce (self-hosted or cloud) requires you to manage SSL certificates at the server or cloud infrastructure level.
- For Adobe Commerce Cloud (PaaS): log in to the Cloud Console → select your project/environment → Domains. Add or update your custom domain and Adobe will provision a Let's Encrypt certificate via Fastly CDN automatically.
- For self-hosted Magento on Apache: copy your new certificate (.crt), private key (.key), and CA bundle (.ca-bundle) to the server. Edit your Apache VirtualHost file (e.g. /etc/apache2/sites-available/yourstore.conf): set SSLCertificateFile, SSLCertificateKeyFile, and SSLCACertificateFile to the correct paths. Restart Apache: sudo systemctl restart apache2.
- For self-hosted Magento on Nginx: update your server block with ssl_certificate and ssl_certificate_key pointing to the new files. Run: sudo nginx -t && sudo systemctl reload nginx.
- In Magento Admin → Stores → Configuration → General → Web, ensure 'Base URL (Secure)' is set to https:// with your correct domain, and 'Use Secure URLs on Storefront' and 'Use Secure URLs in Admin' are both set to Yes.
- Run bin/magento cache:flush after making Admin configuration changes.
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 Adobe Commerce (Magento)
- Adobe Commerce (self-hosted or cloud) requires you or your hosting/DevOps team to manage SSL certificates at the server or load balancer level.
- For Adobe Commerce Cloud: log in to the Cloud Console (cloud.magento.com), select your project and environment, go to Settings → Variables or contact Adobe Support to upload a new SSL certificate via the CLI: `magento-cloud domain:update yourstore.com --cert=cert.pem --key=key.pem --chain=chain.pem`.
- For self-hosted Magento: access your server and use your CA (e.g. Let's Encrypt / Certbot) to issue a new certificate: `sudo certbot --apache -d yourstore.com -d www.yourstore.com` or the nginx equivalent.
- Ensure the certificate SANs cover all domain variants your store uses (bare domain, www, and any subdomains).
- In Magento Admin → Stores → Configuration → Web → Base URLs (Secure), confirm the base URL starts with https:// and matches the exact domain on the certificate.
- Flush Magento's cache (Admin → System → Cache Management → Flush Cache) after any URL or SSL changes.
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 Adobe Commerce (Magento)
- Adobe Commerce (self-hosted or cloud) requires you to configure SSL at the server and application level.
- Server level: ensure port 443 is open in your server's OS firewall (`ufw allow 443/tcp`) and any cloud security group (AWS Security Group, GCP Firewall Rule).
- Install/renew your TLS certificate: use Let's Encrypt via Certbot (`sudo certbot --nginx -d yourdomain.com`) or upload your purchased certificate via your hosting control panel.
- In Magento Admin → Stores → Configuration → General → Web → Base URLs (Secure), set the Secure Base URL to https://yourdomain.com/.
- Also set 'Use Secure URLs on Storefront' and 'Use Secure URLs in Admin' to 'Yes' in the same configuration section.
- Run `bin/magento cache:flush` from the command line to apply the changes, then verify https:// loads correctly.
- For Adobe Commerce Cloud, SSL and port 443 are managed by Fastly CDN — configure the domain in the Cloud Console and Fastly will handle certificate provisioning automatically.
Weak spfQuick win
Add a hard-fail (-all) or soft-fail (~all) mechanism to your SPF DNS record so that mail servers are explicitly told to reject or flag email from senders not listed in your record.
On Adobe Commerce (Magento)
- SPF is a DNS record — it is managed at your DNS/registrar provider, not inside Adobe Commerce's admin panel.
- Log in to your DNS provider or hosting control panel (cPanel/Plesk DNS Zone Editor).
- Find the TXT record for @ (root domain) starting with v=spf1.
- Adobe Commerce typically sends email via your server's mail agent (Sendmail/Postfix) or a third-party SMTP service configured under Stores → Configuration → Advanced → System → Mail Sending Settings. Ensure the sending IP/service is included in the SPF record before appending -all.
- Example: v=spf1 ip4:YOUR.SERVER.IP include:sendgrid.net -all
- Save the record and confirm with an SPF lookup tool.
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 Adobe Commerce (Magento)
- SSH into your server and open your Nginx virtual host config (typically at `/etc/nginx/sites-available/your-store.conf` or inside `/etc/nginx/conf.d/`) or your Apache `.htaccess` / virtual host config.
- For Nginx, add inside the `server {}` block: `add_header X-Content-Type-Options "nosniff" always;` — remove any duplicate `add_header X-Content-Type-Options` lines in the same or parent blocks.
- For Apache, in `.htaccess` or VirtualHost config add: `<IfModule mod_headers.c>` / `Header set X-Content-Type-Options "nosniff"` / `</IfModule>` — remove any duplicate directives.
- Alternatively, configure it in Magento's `pub/.htaccess` or via a custom plugin/module that sets response headers.
- Reload/restart the web server (`sudo nginx -t && sudo systemctl reload nginx` or `sudo systemctl reload apache2`).
- Verify with DevTools or securityheaders.com.
X frame options weakQuick win
Change the X-Frame-Options response header from its current weak or missing value to either DENY or SAMEORIGIN so your store cannot be embedded in a malicious iframe.
On Adobe Commerce (Magento)
- In the Magento admin panel go to Stores → Configuration → General → Web → Default Pages — look for any built-in header setting (varies by version).
- The most reliable method: open your Nginx vhost config for Magento (typically /etc/nginx/sites-available/your-store.conf) and inside the server {} block add: add_header X-Frame-Options "DENY" always; Reload Nginx: sudo nginx -s reload
- For Apache: open your .htaccess or VirtualHost config and add: Header always set X-Frame-Options "DENY" Then restart Apache.
- Alternatively install the Magento 2 module 'magento2-security-headers' via Composer: composer require your-vendor/security-headers, then run bin/magento setup:upgrade and configure in admin.
- Verify by loading your storefront and inspecting response headers in DevTools.
Site Lifecycle · 12 fixes
Cms versionModerate effort
Identify your ecommerce platform and CMS version, then ensure it is always kept up to date to protect your store from security vulnerabilities and avoid loss of vendor support.
On Adobe Commerce (Magento)
- SSH into your server and run `php bin/magento --version` to confirm your current Adobe Commerce or Magento Open Source version.
- Check the Adobe Commerce release schedule and security advisories at the official Adobe Commerce developer documentation to identify the latest supported version.
- Use Composer to update: run `composer update magento/product-community-edition --with-all-dependencies` (or the Commerce equivalent) in your project root after backing up files and database.
- Run `php bin/magento setup:upgrade`, `php bin/magento setup:di:compile`, and `php bin/magento setup:static-content:deploy` after updating.
- Go to Admin → System → Web Setup Wizard (older versions) or use the Quality Patches Tool to apply individual security patches between major releases.
- Review and update all third-party extensions via Composer or the Adobe Commerce Marketplace after the core update.
Domain expiryQuick win
Enable auto-renew on your domain registration and set calendar reminders well before expiry to prevent accidental loss of your store's address.
On Adobe Commerce (Magento)
- Adobe Commerce does not manage domain registrations. Identify which registrar holds your domain (run a WHOIS lookup if unsure — search 'WHOIS lookup' and enter your domain).
- Log in to that registrar, navigate to domain management, and enable auto-renew.
- Ensure billing and contact details are up to date at the registrar level.
- Document the registrar credentials and expiry dates in your team's operational runbook or password manager.
Gtm auditModerate effort
Install Google Tag Manager on your store and configure GA4 with ecommerce event tracking (view_item, add_to_cart, purchase) so you can measure what's driving revenue.
On Adobe Commerce (Magento)
- Install the GTM container snippet via a Magento extension such as 'MagePal Google Tag Manager' (free, available on GitHub/Packagist) or 'Tagalys GTM' for a managed solution.
- Run: composer require magepal/magento2-google-tag-manager and then php bin/magento setup:upgrade in your Magento CLI.
- Configure the extension in Admin → Stores → Configuration → MagePal → Google Tag Manager: enter your GTM Container ID and enable enhanced ecommerce (automatically pushes view_item, add_to_cart, purchase to the dataLayer).
- Alternatively, add GTM snippets manually to app/design/frontend/<Vendor>/<theme>/Magento_Theme/templates/root.phtml in the <head> and after the opening <body> tag.
- In GTM, create GA4 Configuration and Event tags. Verify with GTM Preview and GA4 DebugView, flush the Magento cache (System → Cache Management → Flush Cache Storage), then publish.
Html langQuick win
Add a correct `lang` attribute to your site's `<html>` tag so browsers, search engines, and assistive technologies know what language your store is written in.
On Adobe Commerce (Magento)
- The <html> tag with lang is rendered by the root page template. Locate the file: app/design/frontend/<Vendor>/<Theme>/Magento_Theme/templates/root.phtml (for custom themes) or the equivalent in vendor/magento/module-theme/view/frontend/templates/root.phtml.
- Open root.phtml in your custom theme (never edit core files directly) and find the <html tag.
- Magento provides a helper to output locale/language info. Add the lang attribute using the store's locale: <html lang="<?= $block->escapeHtmlAttr(strstr($currentLocale, '_', true)) ?>"> — or hard-code the BCP 47 tag if you have a single-language store, e.g., <html lang="en">.
- For a cleaner approach, use the Locale Resolver: inject \Magento\Framework\Locale\ResolverInterface in a custom block and output the language code.
- After saving, run bin/magento cache:clean and bin/magento setup:static-content:deploy, then verify via View Page Source on the frontend.
Lifecycle oos schema not updatedModerate effort
Update the `offers.availability` field in your Product schema to `OutOfStock` (or `PreOrder`/`Discontinued`) whenever a product sells out, so Google's data matches your real inventory.
On Adobe Commerce (Magento)
- Adobe Commerce (Magento) generates Product schema through its built-in structured data module or via an SEO extension. Go to Admin > Stores > Configuration > Catalog > Catalog > Storefront to review schema settings.
- If using the native schema output (Magento 2.3+), availability is derived from the product's stock status. Ensure the product's stock status is set to 'Out of Stock': go to Admin > Catalog > Products > edit the product > Quantity and Stock Status section > set 'Stock Status' to 'Out of Stock'.
- For bulk updates: Admin > Catalog > Products > filter by stock status > select affected products > Actions > Update Attributes > set 'Stock Status' to 'Out of Stock'.
- If you use a third-party SEO or schema extension (e.g. Magefan SEO Suite, Amasty SEO Toolkit): check the extension's configuration under Stores > Configuration > [Extension name] > Structured Data, and confirm it maps availability to the live stock status field rather than a static value.
- For custom themes or headless implementations, locate the schema template (typically in 'Magento_Catalog/templates/product/view/schema.phtml' or equivalent JSON-LD template) and verify the availability logic reads '$product->isAvailable()' or the equivalent stock status check.
- Clear the Magento cache (System > Cache Management > Flush Magento Cache) after any changes, then validate with Google's Rich Results Test.
Lifecycle orphaned productsModerate effort
Add internal links from category pages, navigation, and related-product sections to every product page so crawlers and shoppers can find them without relying solely on your sitemap.
On Adobe Commerce (Magento)
- In the Admin panel, go to Catalog → Products. Use the 'Visibility' and 'Categories' column filters (or create a custom filter: Filters → Category → 'No Category') to identify products not assigned to any category.
- Click Edit on each orphaned product, go to the 'Categories' section in the left panel, and assign the product to one or more relevant categories.
- Ensure the categories appear in navigation: go to Catalog → Categories, confirm the relevant categories are 'Active' and 'Include in Navigation Menu' is set to Yes.
- To add related products: on the product edit page, scroll to the 'Related Products, Up-Sells, and Cross-Sells' section and manually add related products pointing to and from the orphaned product.
- For automated related products at scale: install an extension such as 'Magento 2 Automatic Related Products' (e.g. from Mageplaza or Amasty) to build rule-based related product links across your catalog.
- For CMS/blog contextual links: go to Content → Pages or Content → Blocks, edit the relevant page, and use the WYSIWYG editor to insert a hyperlink to the product URL.
Lifecycle products missing from sitemapModerate effort
Add every canonical product URL to your XML sitemap so search engines can discover and index your products faster.
On Adobe Commerce (Magento)
- Go to Admin Panel → Marketing → SEO & Search → Site Map → Add Sitemap.
- Configure the sitemap: set Filename (e.g. sitemap.xml), Path (/), and Store View, then click Save & Generate.
- To control which products are included, go to Stores → Configuration → Catalog → XML Sitemap → Products section. Set 'Frequency' and 'Priority', and ensure products are not excluded.
- For automatic regeneration: Stores → Configuration → Catalog → XML Sitemap → Generation Settings → enable 'Enabled' and set a schedule.
- Submit the sitemap path in Google Search Console → Sitemaps.
Lifecycle products too deepModerate effort
Reduce the number of clicks required to reach every product page to 4 or fewer from the homepage by flattening your site structure or surfacing buried products on shallower category pages.
On Adobe Commerce (Magento)
- Admin → Catalog → Categories: review your category tree. Every additional category level adds a click depth; aim for no more than two sub-levels (Root → Category → Subcategory → Product = 3 clicks).
- To flatten the hierarchy, drag a deep subcategory up one level in the category tree, or edit it and change its 'Parent Category' to a higher-level category.
- Assign buried products to a shallower category: Catalog → Products → Edit product → Categories → add the parent or grandparent category alongside the existing deep one.
- Content → Elements → Widgets: create a 'Catalog Product List' widget and place it on the homepage (or a top-level CMS page) filtered to the deep subcategory, giving those products a 1-click path.
- Content → Elements → Pages → [Homepage] → Edit with Page Builder: add a 'Products' content type block showing the buried subcategory's products directly on the homepage.
- Stores → Configuration → Catalog → Sitemap: ensure sitemap generation includes all products and resubmit to Google Search Console after restructuring. Re-run a crawl to verify depth ≤ 4.
Mixed contentModerate effort
Audit every page, asset, and third-party embed on your store to ensure no HTTP resources are loaded on HTTPS pages, and fix any mixed-content violations before they silently break security warnings or block content in visitors' browsers.
On Adobe Commerce (Magento)
- In the admin panel, go to Stores → Configuration → General → Web.
- Under 'Base URLs (Secure)', set the Secure Base URL to https:// and ensure 'Use Secure URLs on Storefront' and 'Use Secure URLs in Admin' are both set to 'Yes'. Save Config.
- Go to Stores → Configuration → General → Web → Base URLs and confirm the non-secure Base URL also redirects to HTTPS (handle this via your server's .htaccess or nginx config with a 301 redirect).
- Run the Magento URL reindex and clear the full-page cache: in the admin go to System → Cache Management → Flush Magento Cache, or via CLI: php bin/magento cache:flush.
- Use a database search-replace tool or a Magento module (e.g. the free 'MagePal HTTP to HTTPS Redirect' or similar) to update any hardcoded HTTP URLs stored in the catalog, CMS pages, and widgets tables.
- Check any third-party extensions that inject scripts or assets (System → Integrations, or custom modules in app/code) for hardcoded HTTP asset URLs and update them.
- Add the Content-Security-Policy upgrade-insecure-requests header in your server config (Apache: Header always set Content-Security-Policy 'upgrade-insecure-requests'; Nginx: add_header Content-Security-Policy 'upgrade-insecure-requests';) or via a plugin.
- Flush all caches again and verify with browser DevTools that no mixed-content warnings remain.
Mobile viewportQuick win
Confirm your store has a correct responsive viewport meta tag so it displays properly on phones and tablets.
On Adobe Commerce (Magento)
- The viewport meta tag is output via your theme's layout XML and PHTML templates. Navigate to your theme directory: app/design/frontend/<Vendor>/<Theme>/.
- Open Magento_Theme/layout/default_head_blocks.xml (or the equivalent file in your theme). Look for a <meta name='viewport'> declaration.
- Alternatively, check app/design/frontend/<Vendor>/<Theme>/Magento_Theme/templates/root.phtml for a hardcoded viewport tag in the <head>.
- Ensure the content attribute reads exactly: width=device-width, initial-scale=1. Remove any user-scalable=no or maximum-scale restrictions.
- If the tag is missing, add it in default_head_blocks.xml using: <block class="Magento\Framework\View\Element\Html\Head\Meta" name="viewport"><arguments><argument name="content" xsi:type="string">width=device-width, initial-scale=1</argument></arguments></block>
- Run bin/magento cache:flush, then verify with Google's Mobile-Friendly Test.
Server versionModerate effort
Remove or suppress the Server version header so your web server software and version number are no longer exposed in every HTTP response.
On Adobe Commerce (Magento)
- Adobe Commerce (cloud) uses Fastly as its CDN/edge layer. Log into the Magento Cloud Admin or Fastly control panel and configure a 'Response Header' VCL snippet to unset the Server header: in the Fastly VCL, add `unset beresp.http.Server; unset beresp.http.X-Powered-By;` in the `vcl_fetch` subroutine.
- For Adobe Commerce (on-premises) on Nginx: edit /etc/nginx/nginx.conf or the site vhost config, add `server_tokens off;` in the `http {}` block, and use the headers_more module to fully remove the header: `more_clear_headers 'Server';`.
- For on-premises Apache: edit httpd.conf (or the vhost config), set `ServerTokens Prod` and `ServerSignature Off`, and add `Header unset X-Powered-By`.
- For PHP version exposure: in php.ini (or via .htaccess on Apache with `php_flag expose_php Off`), set `expose_php = Off`.
- Reload/restart Nginx or Apache: `sudo systemctl reload nginx` or `sudo systemctl reload apache2`.
- Verify with browser DevTools or `curl -I https://yourstore.com` and confirm no version string is present in the Server header.
Ssl expiryQuick win
Monitor your SSL/TLS certificate expiry date and set up auto-renewal so your store never goes offline or shows a security warning to shoppers.
On Adobe Commerce (Magento)
- SSL is managed at the server/hosting level, not within Magento itself.
- Log into your server's control panel (cPanel, Plesk) or cloud hosting dashboard (AWS, Azure, GCP, Nexcess, etc.) and locate the SSL/TLS section.
- Confirm auto-renewal is enabled for Let's Encrypt, or note the expiry date of your paid certificate and set a 60-day renewal reminder.
- After installing a renewed certificate, log into the Magento Admin → Stores → Configuration → General → Web → Base URLs (Secure) and confirm all URLs use https://.
- Run 'bin/magento cache:flush' from the command line after any SSL/URL configuration change.