Every WooCommerce fix we catalog

SEOLZ catalogs 167 fixes for WooCommerce across 6 areas — SEO, answer-engine readiness, accessibility, security and site-health. Each lists the exact steps for WooCommerce, 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 WooCommerce

  1. Sitemap: Install Yoast SEO or Rank Math (free). In Yoast: SEO → General → Features → turn on XML Sitemaps. In Rank Math: Rank Math → Sitemap Settings → enable Product and Product Category sitemaps. Submit the sitemap URL to Google Search Console.
  2. robots.txt: In Yoast go to SEO → Tools → File Editor to view/edit robots.txt. Ensure no Disallow covers /shop/, /product/, /product-category/, or /store/.
  3. Noindex: In Yoast, go to SEO → Search Appearance → Taxonomies → Product Categories and make sure 'Show in search results' is set to Yes. Do the same under Post Types → Products.
  4. Internal linking: Ensure every product is assigned to at least one Product Category (Products → [product] → Product categories). Add your shop page and top categories to Appearance → Menus → Primary Menu.
  5. Pagination/filtering: If you use AJAX-based product filtering plugins (e.g. WooCommerce Product Filters), verify the plugin outputs crawlable URLs (standard query strings or pretty permalinks) rather than pure JS state changes.
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 WooCommerce

  1. Images: In Appearance > Customize > (theme options) or directly in your theme's template files (e.g. woocommerce/content-single-product.php), ensure every <img> has width and height attributes. WordPress auto-generates these for images added via the Media Library — verify your theme is not stripping them.
  2. WordPress Core: Navigate to Settings > Media and confirm thumbnail dimensions are set. When regenerating thumbnails (use the 'Regenerate Thumbnails' plugin), WordPress re-writes width/height into the database markup.
  3. Fonts: In Appearance > Theme File Editor (or via a child theme's style.css), add font-display: swap; to every @font-face rule. If your theme uses Google Fonts, install the 'OMGF | Host Google Fonts Locally' plugin to self-host fonts and apply font-display:swap from its settings panel.
  4. Third-party plugins (chat, reviews, pop-ups): Go to each plugin's settings and ensure their widgets are positioned fixed/sticky or loaded below the fold. Move any <script> tags these plugins add to the footer using a plugin like 'Asset CleanUp' to defer or move them.
  5. Page speed plugin: Install a performance plugin such as WP Rocket, LiteSpeed Cache, or NitroPack. In the plugin's settings, enable 'Eliminate render-blocking resources,' 'Defer JavaScript,' and (in WP Rocket) the 'Remove Unused CSS' option — these collectively reduce the late-loading surprises that cause CLS.
  6. Ads/banners: If you use a banner or slider plugin (e.g. MetaSlider, Slider Revolution), set an explicit height on the slider container in CSS, or use the plugin's built-in 'aspect ratio' option, so the space is reserved before the images load.
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 WooCommerce

  1. Install the 'Perfmatters' or 'WP Rocket' plugin (Plugins → Add New) to handle render-blocking JS/CSS deferral and preloading in one interface.
  2. Convert your hero image to WebP: install the 'Imagify' or 'ShortPixel' plugin, which auto-converts uploaded images to WebP and serves them via <picture> elements with a JPEG/PNG fallback.
  3. In your active theme's header.php (Appearance → Theme File Editor → header.php), add a preload link tag for the hero image URL: <link rel='preload' as='image' href='YOUR-HERO-IMAGE-URL.webp' fetchpriority='high'>. If using a page builder (Elementor/Divi), use the theme's Custom Code / Head section instead.
  4. In WP Rocket (Settings → WP Rocket → Media tab), enable 'Disable LazyLoad for LCP image' and paste the CSS selector of your hero image so it loads eagerly.
  5. Under WP Rocket → File Optimization, enable 'Minify CSS', 'Load CSS asynchronously', 'Defer JavaScript', and 'Delay JavaScript execution' to remove render-blocking resources.
  6. Verify: use PageSpeed Insights or the WP Rocket built-in 'Sitechecker' to confirm LCP is under 2.5s on mobile.
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 WooCommerce

  1. Install an SEO plugin if not already present — Yoast SEO or Rank Math are the most common choices.
  2. For individual products: Admin → Products → Edit product → scroll to the 'Yoast SEO' (or 'Rank Math SEO') meta box below the editor → click the 'Edit snippet' preview → type a unique description in the 'Meta description' field → Update.
  3. For shop/category pages: Admin → Products → Categories → Edit category → fill in the SEO meta box 'Meta description' at the bottom → Save.
  4. For the homepage: Yoast → Settings → Site basics → Homepage, or navigate to the homepage in the editor and use the page-level Yoast meta box.
  5. For bulk updates: use Yoast SEO's bulk editor (Yoast → Tools → Bulk editor → Descriptions tab) to edit descriptions for all posts/pages in one table view without opening each individually.
  6. Alternatively, use Rank Math → Content AI or the built-in bulk editing screen under Rank Math → Analytics → Posts for mass updates.
Duplicate titleModerate effort

Write a unique, descriptive title tag for every page on your store so no two pages share the same title.

On WooCommerce

  1. Install and activate the Yoast SEO or Rank Math plugin if not already present.
  2. For individual products/pages: open the product or page editor, scroll to the Yoast SEO / Rank Math meta box, click the 'Edit snippet' button, and fill in a unique 'SEO title'.
  3. For site-wide templates (all products, all categories, etc.): go to Yoast SEO → Search Appearance → Content Types (or Rank Math → Titles & Meta → Post Types) and set a dynamic template using variables like %%title%% %%sep%% %%sitename%% to ensure unique output per page.
  4. For bulk updates: use Yoast SEO's bulk editor (Yoast SEO → Tools → Bulk editor) or Rank Math's SEO Analyzer to find and fix pages with missing or duplicate titles.
  5. If you use a theme that hardcodes the <title> tag in header.php, replace it with wp_title() or rely on the SEO plugin's output_buffer method — your SEO plugin's documentation covers this.
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 WooCommerce

  1. Install or confirm you have Yoast SEO, Rank Math, or All in One SEO installed — these plugins manage canonical tags site-wide.
  2. In Yoast SEO: go to SEO → Search Appearance → Taxonomies. For product categories, confirm 'Show in search results' is enabled (this sets the canonical correctly for the base category).
  3. For filter URLs generated by WooCommerce layered nav or a plugin like FacetWP or WOOF: in FacetWP go to Settings → General → set 'Canonical URL' to 'Parent page' so filtered results point to the base archive URL. In WOOF, look for its SEO/canonical option in the plugin settings.
  4. If using Yoast SEO, go to SEO → Tools → File Editor and add `Disallow:` rules in robots.txt for heavily-parameterized filter paths you want to block from crawling entirely (e.g., `Disallow: /*?filter_*`).
  5. Alternatively, add noindex to filtered pages by hooking into `wpseo_robots` (Yoast) or using Rank Math's 'noindex' condition for archive pages with query parameters.
  6. Verify by visiting a filtered URL, viewing page source, and confirming `<link rel='canonical'>` points to the base 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 WooCommerce

  1. FOR PRODUCT IMAGES: In the WordPress admin, go to Products > All Products, open a product, scroll to the 'Product image' box (bottom right), click the image, then click the pencil (Edit) icon. In the Attachment Details panel that appears, fill in the 'Alt Text' field and click 'Update'.
  2. FOR GALLERY IMAGES: On the same product page, scroll to 'Product gallery', click a gallery image's pencil icon, and fill in the Alt Text field.
  3. FOR ALL MEDIA LIBRARY IMAGES: Go to Media > Library, click any image, and update the 'Alternative Text' field in the right-hand Attachment Details panel.
  4. FOR BULK EDITS: Install the free 'SEO Optimized Images' plugin or use 'Rank Math SEO' / 'Yoast SEO' which include bulk alt-text tools under their SEO > Tools menus.
  5. FOR CONTENT IMAGES: When editing a page or post in the Block Editor, click an Image block and fill in the 'Alt text (alternative text)' field in the right-hand Block settings panel.
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 WooCommerce

  1. Hosting — Switch to a host with LiteSpeed or Nginx + object caching (Redis/Memcached) if you're on shared Apache hosting; this alone can double your score.
  2. Caching & optimization plugin — Install WP Rocket (paid, easiest all-in-one) or the free stack: LiteSpeed Cache OR W3 Total Cache + Autoptimize. Configure: page caching ON, CSS/JS minification ON, defer JS ON, lazy-load images ON.
  3. Image optimization — Install ShortPixel or Imagify: Dashboard → ShortPixel → Bulk Optimize to convert existing images to WebP and compress them. Set all new uploads to auto-optimize.
  4. Remove plugin bloat — Dashboard → Plugins → deactivate and delete any unused plugins. Use Query Monitor or Asset CleanUp Pro to identify which plugins add scripts to which pages, then disable per-page.
  5. CDN — In your caching plugin's CDN tab, connect Cloudflare (free) or BunnyCDN to serve static assets from edge nodes worldwide.
  6. Core theme/block editor — If using a page builder (Elementor, Divi), check its own performance settings (Elementor: Dashboard → Elementor → Performance → enable Improved Asset Loading and Inline Critical CSS).
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 WooCommerce

  1. Install or confirm you have an SEO plugin active — Yoast SEO, Rank Math, or All in One SEO are the most common.
  2. For Yoast SEO: open the product, page, or post in the WordPress block editor. Scroll to the 'Yoast SEO' panel at the bottom. Click the 'SEO' tab, then edit the 'Meta description' field. The snippet preview and character counter show when you're within the safe range (green bar).
  3. For Rank Math: open the post/page editor, click the Rank Math icon (top-right toolbar), go to the 'General' tab, and edit the 'Description' field. A live character counter is shown.
  4. For site-wide defaults (e.g. category archives): in Yoast go to Yoast SEO → Search Appearance → Taxonomies or Content Types and edit the default description template there.
  5. Click 'Update' or 'Publish' to save changes.
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 WooCommerce

  1. Install the Yoast SEO plugin (or Rank Math SEO) if not already active: Plugins → Add New → search 'Yoast SEO' → Install → Activate.
  2. For a product: Products → All Products → Edit the product → scroll to the 'Yoast SEO' meta box below the editor → click the 'Google preview' snippet editor → type your description in the 'Meta description' field; the colour bar turns green at 120–156 characters → Update.
  3. For a category/tag page: Products → Categories → Edit the category → scroll to the Yoast SEO box → update the Meta description → Save.
  4. For standard pages/posts: Pages → Edit → Yoast SEO box → Meta description → Update.
  5. For bulk edits: use Yoast SEO's Bulk Editor (SEO → Tools → Bulk editor → Descriptions tab) to update multiple pages from one screen.
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 WooCommerce

  1. Install the free Yoast SEO or Rank Math plugin if not already installed (Plugins → Add New).
  2. Yoast SEO: Go to SEO → Search Appearance; canonicals are output automatically for all post types including Products — no additional setup needed for self-referencing canonicals.
  3. Rank Math: Go to Rank Math → Titles & Meta → Products; canonicals are enabled by default.
  4. To set a custom canonical on a specific product or page, open that post/product in the editor, scroll to the Yoast SEO or Rank Math meta box, click the 'Advanced' tab, and enter the canonical URL in the 'Canonical URL' field.
  5. Verify by viewing page source or using the browser extension for Yoast/Rank Math to confirm the tag is present.
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 WooCommerce

  1. **Product pages:** The WooCommerce default (Storefront and most themes) renders the product name as `<h1 class="product_title">`. If your theme overrides this, go to Appearance → Theme File Editor → open 'woocommerce/single-product/title.php' (copy it to your child theme first) and confirm the tag is `<h1>`.
  2. **Shop / Category pages:** Appearance → Customize → WooCommerce → Product Catalog — some themes let you toggle the page title here. In template files, check 'woocommerce/archive-product.php' and ensure `woocommerce_page_title()` is wrapped in `<h1>`.
  3. **Regular pages & posts:** Edit the page in Gutenberg (or Classic Editor) → the 'Title' field at the top of the editor IS the H1 — simply ensure every page has a title entered.
  4. **SEO plugin override:** If you use Yoast SEO or Rank Math, go to the post/page edit screen → scroll to the SEO plugin panel → confirm the focus keyphrase matches the H1 text; these plugins do not change the H1 themselves but their analysis flags a missing one.
  5. Use the free 'Headings Map' browser extension to verify exactly one H1 appears on each page after saving.
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 WooCommerce

  1. Install the free Yoast SEO or Rank Math plugin if not already active (WordPress Admin → Plugins → Add New → search → Install & Activate).
  2. For a product: Admin → Products → click the product → scroll to the 'Yoast SEO' or 'Rank Math SEO' meta box below the editor → click the 'Edit snippet' button → type your meta description in the 'Meta description' field → Update.
  3. For a category: Admin → Products → Categories → click 'Edit' on the category → scroll to the Yoast/Rank Math box at the bottom → fill Meta description → Save.
  4. For the homepage (if set to a static page): Admin → Pages → edit the homepage → use the same Yoast/Rank Math meta box.
  5. For the WooCommerce shop page: Admin → SEO (Yoast) → Search Appearance → WooCommerce tab → set a template or override for the Shop page description.
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 WooCommerce

  1. Install the free Yoast SEO or Rank Math plugin if not already active (Plugins → Add New → search and install).
  2. For Yoast SEO: edit any product, page, or post → scroll to the 'Yoast SEO' meta box → click the 'Social' tab → enter your og:description in the 'Facebook description' field, or leave it blank to inherit from the SEO meta description field on the 'SEO' tab.
  3. For Rank Math: edit the item → open the Rank Math meta box → go to the 'Social' tab → fill in 'Facebook Description'.
  4. To set a site-wide fallback in Yoast: Yoast SEO → Social → Facebook → enable Open Graph → save. Yoast will then fall back to the meta description for og:description when no explicit value is set.
  5. For programmatic control in your theme, add to functions.php or a custom plugin (only if not using an SEO plugin): add_action('wp_head', function(){ if(is_product()){ global $post; echo '<meta property="og:description" content="' . esc_attr(get_the_excerpt()) . '">'; } });
  6. 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 WooCommerce

  1. Install and activate the free 'Yoast SEO' plugin (or 'Rank Math SEO') from Plugins → Add New — these are the standard tools for managing og:image in WordPress/WooCommerce.
  2. For Yoast SEO: go to each Product edit screen, scroll to the 'Yoast SEO' meta box → click the 'Social' tab → upload or select a Facebook image (og:image). If left blank, Yoast falls back to the product's featured image automatically.
  3. To set a site-wide fallback og:image in Yoast: go to SEO (Yoast) → Social → Facebook tab → upload a default image under 'Default image for Facebook sharing'.
  4. For Rank Math: edit a product, scroll to the 'Rank Math SEO' panel → Social Media tab → set the 'Facebook Thumbnail' image.
  5. Ensure every WooCommerce product has a featured image set (Product → Product image, top-right of the edit screen), as this is the automatic fallback for both plugins.
  6. After making changes, validate using the Meta Sharing Debugger to confirm the og:image is rendering correctly for each page type.
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 WooCommerce

  1. The easiest approach is to use an SEO plugin. Install and activate Yoast SEO or Rank Math from WordPress admin → Plugins → Add New.
  2. For Yoast SEO: go to SEO → Social → Facebook tab, ensure 'Add Open Graph meta data' is toggled ON. Yoast will then automatically output og:title on every page using that page's SEO title.
  3. For Rank Math: go to Rank Math → Titles & Meta → each post type (Products, Categories, etc.) and confirm the 'Social Media' panel is enabled under Rank Math → General Settings → Links.
  4. To customise the og:title for an individual product, open the product in the editor, scroll to the Yoast SEO (or Rank Math SEO) meta box → Social tab → enter a custom Facebook title.
  5. If you prefer code, add the og:title output to your theme's header.php inside the <head> tag, but using a plugin is strongly recommended to avoid breaking updates.
  6. Validate with Facebook Sharing Debugger after saving.
Missing titleQuick win

Add a unique, descriptive title tag (30–60 characters) to every page that is missing one.

On WooCommerce

  1. Install and activate the free Yoast SEO or Rank Math plugin if not already present — these are the standard tools for managing title tags in WordPress/WooCommerce.
  2. For a product: go to Products → All Products → select the product → scroll to the Yoast SEO (or Rank Math) meta box below the editor → click the 'SEO title' field and type your title.
  3. For a shop/category page: go to Products → Categories → select the category → scroll to the Yoast/Rank Math section at the bottom of the edit screen → fill in the SEO title.
  4. For the homepage or a static page: go to Pages → All Pages → select the page → use the Yoast/Rank Math meta box.
  5. For global title templates (e.g. auto-format for all products): go to Yoast SEO → Settings → Content types → Products, or Rank Math → Titles & Meta → Products, and set a template like '%title% – %sitename%'.
  6. Click Update/Publish and verify in the page source that <title> contains your intended text.
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 WooCommerce

  1. In WordPress admin, go to Appearance → Theme File Editor (or use a child theme via FTP/cPanel).
  2. Open the relevant template file for the page type: single.php or content-single.php for posts, page.php for pages, woocommerce/single-product.php (copy to your child theme first) for products.
  3. Search for '<h1' in each file and ensure only the primary title uses it — the_title() or get_the_title() wrapped in <h1>.
  4. If using a page builder (Elementor, Divi, Beaver Builder): open the page in the builder, click each heading widget, and change 'HTML Tag' from H1 to H2/H3 for all non-primary headings.
  5. Check your theme's header.php for any H1 tags around the site name or tagline; switch them to <p> or <span> with matching CSS styling.
  6. Install the free 'Rank Math' or 'Yoast SEO' plugin — both include an on-page analysis that flags multiple H1s, making it easy to find and fix remaining instances.
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 WooCommerce

  1. The most common source is the Yoast SEO or Rank Math plugin's per-page setting.
  2. For Yoast SEO: edit the affected page/post/product in WordPress → scroll to the Yoast SEO meta box → click the 'Advanced' tab → check the 'Allow search engines to show this post in search results?' dropdown — set it to 'Yes' (or 'Default for posts').
  3. For Rank Math: edit the page → open the Rank Math meta box → Advanced tab → 'Robots Meta' section — ensure 'No Index' checkbox is NOT checked.
  4. For site-wide settings in Yoast: WordPress Admin → SEO → Search Appearance → Content Types / Taxonomies — ensure the relevant post type is set to 'Show in search results: Yes'.
  5. Also check Settings → Reading → confirm 'Discourage search engines from indexing this site' is NOT checked (this adds a noindex to every page).
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 WooCommerce

  1. WooCommerce relies on WordPress/Yoast/Rank Math to output canonical tags.
  2. If you use Yoast SEO: go to WordPress Admin → SEO (Yoast) → Search Appearance → check 'Advanced' settings. Per-page overrides live in the Yoast meta box at the bottom of each page/product editor → Advanced tab → 'Canonical URL' field. Clear any incorrect value.
  3. If you use Rank Math: WordPress Admin → Rank Math → Edit the page/product → the 'Advanced' tab in the Rank Math meta box contains a 'Canonical URL' field. Clear incorrect values.
  4. If canonical logic is in your theme, check your child theme's functions.php or a custom plugin for wp_head hooks that output rel=canonical.
  5. After fixing, use the URL Inspection tool in Google Search Console to confirm the canonical is now self-referencing.
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 WooCommerce

  1. Caching: Install and activate a caching plugin such as WP Rocket, W3 Total Cache, or LiteSpeed Cache (if on LiteSpeed hosting). In the plugin settings enable page caching, browser caching, and GZIP compression.
  2. Images: Install 'Imagify', 'ShortPixel', or 'Smush' (Plugins → Add New, search by name). Run bulk optimisation on your Media Library and enable WebP conversion and lazy loading.
  3. Scripts/CSS: In your caching plugin's 'File Optimisation' or 'Minify' tab, enable Minify CSS, Combine CSS, Minify JS, and Defer JS loading. Test checkout pages carefully after enabling JS deferral.
  4. CDN: In your caching plugin's CDN tab (or separately via Cloudflare — add your domain at cloudflare.com and update your nameservers), enable CDN delivery for static assets.
  5. Hosting: Upgrade to managed WordPress/WooCommerce hosting (e.g. Kinsta, WP Engine, Cloudways) if you are on shared hosting — server response time (TTFB) is often the single largest bottleneck.
  6. Verify: Run Google PageSpeed Insights before and after. In WP Rocket, use the 'Rocket' toolbar item → 'Clear cache' after every change.
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 WooCommerce

  1. Install the free Yoast SEO or Rank Math plugin if not already installed.
  2. In Yoast SEO: go to SEO → Search Appearance → Archives → Search Pages and toggle 'Show search pages in search results?' to OFF (sets noindex automatically).
  3. In Rank Math: go to Rank Math → Titles & Meta → Search Results and enable 'No Index'.
  4. For robots.txt: go to SEO → Tools → File Editor (Yoast) or Rank Math → General Settings → Edit robots.txt, and add: Disallow: /?s= (WordPress default search uses ?s= query parameter).
  5. Verify by visiting /?s=test on your store and checking the page source for the noindex meta tag.
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 WooCommerce

  1. In your WordPress admin, go to Products → Categories.
  2. Click 'Edit' on the category you want to update.
  3. In the 'Description' field (a plain text or visual editor area, depending on your theme/plugin), enter your 100–200 word description.
  4. If you want a full visual editor (Gutenberg blocks) for the description, install the 'Category and Taxonomy Image' plugin or use a theme that supports rich category descriptions.
  5. Click 'Update'. Your theme controls where the description is displayed — most themes show it above the product grid; check your theme's documentation if it doesn't appear.
  6. Optionally use Yoast SEO or Rank Math to set a separate meta description for the category in the same edit screen.
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 WooCommerce

  1. Go to WP Admin → Products → Categories.
  2. Click 'Edit' next to the category you want to update.
  3. In the 'Description' field (a full WordPress block/TinyMCE editor), write your expanded content.
  4. Click 'Update'.
  5. Confirm the description displays on the front-end category archive page — if it doesn't, your theme may suppress it. In that case, add the following to your child theme's 'archive-product.php' or via a shortcode plugin: <?php echo term_description(); ?>.
  6. For block themes, use the Site Editor (Appearance → Editor) to add a 'Term Description' block to the Product Category template.
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 WooCommerce

  1. To ADD products: WordPress Admin → Products → All Products → edit each relevant product → scroll to 'Product categories' in the right sidebar → check the empty category → Update.
  2. To NOINDEX with Yoast SEO: Products → Categories → hover the empty category → click 'Edit' → scroll to the Yoast SEO meta box → Advanced tab → set 'Allow search engines to show this category in search results?' to 'No' → Save.
  3. To NOINDEX with Rank Math: Products → Categories → edit the category → Rank Math SEO panel → Advanced → Robot Meta → check 'No Index' → Save.
  4. To HIDE from nav: Appearance → Menus → find the category item → click the arrow to expand it → Remove → Save Menu. Also consider unchecking it in WooCommerce → Settings → Products → Display if it appears in widget-based category lists.
  5. To DELETE and REDIRECT: Products → Categories → hover → Delete. Then use the 'Redirection' plugin: Tools → Redirection → Add new redirect from the old URL to the parent category.
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 WooCommerce

  1. In WordPress admin, go to Products → Categories to see all categories and their product counts.
  2. To add products to the sparse category: open each relevant product (Products → All Products → Edit), scroll to 'Product Categories' in the right sidebar, and tick the correct category.
  3. To add a category description: go to Products → Categories, click Edit on the sparse category, and fill in the 'Description' field — this text appears on the category archive page if your theme supports it.
  4. To merge: reassign all products to the target category as above, then set up a 301 redirect from the old category slug to the new one using a plugin such as Redirection (Settings → Redirection → Add Redirect).
  5. After all products are moved, delete the empty old category from Products → Categories.
  6. 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 WooCommerce

  1. If you use Yoast SEO (most common): Go to Yoast SEO → Search Appearance → Taxonomies. Confirm product categories are set to index. Yoast automatically adds self-referencing canonicals to category pages, but for filter plugin URLs you need additional configuration.
  2. If you use the WooCommerce Layered Nav or FiboFilters / WOOF / JetSmartFilters plugin for faceted filtering, check the plugin's settings for a 'Canonical URL' or 'SEO' option — many modern filter plugins include this natively.
  3. For WooCommerce's own /?orderby= sort parameters: in Yoast SEO go to SEO → Search Appearance → Archives and ensure the canonical for paginated/filtered archives points to the base category. Yoast handles ?orderby= as a non-canonical variant automatically.
  4. For custom or unhandled parameter patterns, add a code snippet to your child theme's `functions.php` or a custom plugin: hook into `wp_head` and output a canonical using `wc_get_page_id` or `get_term_link` to build the clean category URL, then echo the tag only when filter parameters are present in `$_GET`.
  5. Install and configure Rank Math SEO (alternative to Yoast) — it has explicit 'Canonical URL' fields per post type and handles WooCommerce shop/category canonicals under Rank Math → Titles & Meta → WooCommerce.
  6. Test with Google Search Console URL Inspection or a browser plugin like MozBar after saving.
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 WooCommerce

  1. Go to WP Admin → Products → All Products and click 'Edit' on the target product.
  2. Scroll to the main 'Product description' editor (the large WordPress block/classic editor below the product data tabs — not the short description).
  3. Add content using Gutenberg blocks (Paragraph, List, Table, Heading) or the Classic editor toolbar. Aim for 300–600+ words covering specs, use cases, and benefits.
  4. For the short description (shown near the Add to Cart button), also write 2–3 compelling sentences in the 'Product short description' box further down the page.
  5. Install the 'Yoast SEO' or 'Rank Math' plugin to get an in-page readability and keyword-density indicator that flags when content is still too thin.
  6. Click 'Update' when done.
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 WooCommerce

  1. In your WordPress Admin, go to Products → All Products and click Edit on the target product.
  2. Scroll to the large Product Description editor (the full-page WordPress block editor or classic editor area at the top of the edit screen — NOT the shorter 'Product short description' box further down).
  3. Write your 200–400 word description here. The short description box (below Product Data) is a secondary summary; fill it too with 1–2 sentences.
  4. Click Update to publish. WooCommerce automatically outputs this text inside schema.org Product markup via most themes.
  5. If you use Yoast SEO or Rank Math, confirm in those plugins' schema settings that the product description is mapped to the schema 'description' property.
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 WooCommerce

  1. Go to WordPress Admin → Products → All Products → click 'Edit' on the target product.
  2. Use the 'Product description' editor (the large block editor or classic editor panel at the top of the page — not the 'Short description' box at the bottom, which is a brief excerpt).
  3. Write or paste your full expanded description here. Use Gutenberg blocks (paragraph, list, heading) or the classic toolbar to structure content.
  4. The 'Short description' field (below the main editor) is shown near the Add-to-Cart button — keep this concise (1–3 sentences) and put the full content in the main description.
  5. Click 'Update'. WooCommerce renders this as standard HTML, fully readable by Google.
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 WooCommerce

  1. Go to Products → All Products and edit a product.
  2. In the 'Product image' box (right sidebar), click the product's featured image to open the WordPress Media Library.
  3. In the 'Attachment details' panel on the right, fill in the 'Alt Text' field with your descriptive text.
  4. Click 'Update' to save.
  5. For gallery images, scroll to the 'Product gallery' meta box, click each thumbnail, and edit its Alt Text in the same Media Library panel.
  6. For bulk updates, install the free plugin 'SEO Optimized Images' or 'Image SEO' from the WordPress plugin directory to manage alt attributes at scale.
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 WooCommerce

  1. Install a structured data plugin such as 'Rank Math SEO' or 'Schema & Structured Data for WP & AMP' from WordPress admin → Plugins → Add New.
  2. In Rank Math: go to Rank Math → Titles & Meta → Products, set Schema Type to 'Product', then enable the Brand field and map it to a product attribute or custom field.
  3. Alternatively, add a 'Brand' product attribute: WooCommerce → Products → Attributes, create a 'Brand' attribute, then assign it per product under the product's Attributes tab.
  4. If coding manually, edit `functions.php` or a child theme file to filter `woocommerce_structured_data_product` and inject the `brand` property pulling from your chosen attribute or custom meta.
  5. Validate output at 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 WooCommerce

  1. Install the free plugin 'WooCommerce Product GTIN (EAN, UPC, ISBN)' by WPFactory (or a comparable plugin such as 'GTIN for WooCommerce') from Plugins → Add New.
  2. After activation, go to Products → select a product → find the new 'GTIN / EAN / UPC / ISBN' field in the Product Data meta box → enter the barcode number.
  3. The plugin automatically adds the GTIN to the Product structured data output on the frontend.
  4. If you already use a full schema plugin (e.g. 'Rank Math SEO' or 'Schema Pro'), check its Product schema settings under Rank Math → Schema → Product — those plugins have dedicated GTIN/MPN fields you can map to a custom product attribute or meta field.
  5. For Rank Math: go to a product edit screen → Schema tab (bottom of page) → Product schema → fill in 'GTIN' and 'MPN' directly.
  6. 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 WooCommerce

  1. WooCommerce outputs Product schema automatically, but the `availability` field depends on your stock management settings.
  2. In WP Admin, go to WooCommerce → Settings → Products → Inventory and ensure 'Enable stock management' is checked.
  3. On each product edit screen (Products → All Products → Edit), scroll to the 'Inventory' tab in Product Data and set the Stock Status to 'In stock', 'Out of stock', or 'On backorder'.
  4. To enhance or override the schema output, install the free plugin 'Schema & Structured Data for WP & AMP' or 'Rank Math SEO', which let you map WooCommerce stock fields directly to schema `availability` without custom code.
  5. Alternatively, add a snippet to your child theme's `functions.php` using the `woocommerce_structured_data_product` filter to inject or override the `availability` value programmatically.
  6. Verify by viewing page source on a product page and searching for `availability`, or use the Rich Results Test.
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 WooCommerce

  1. In your WordPress admin, go to Products → All Products.
  2. Click 'Edit' on the product missing an image.
  3. In the right-hand sidebar, find 'Product image' (the featured/main image) — click 'Set product image' and upload or select from the media library.
  4. To add a gallery, scroll down to the 'Product gallery' meta box below the editor and click 'Add product gallery images'.
  5. Click the image in the media library, then fill in the 'Alt Text' field on the right before clicking 'Set product image'.
  6. Click 'Update' to save. WooCommerce includes product images in its structured data automatically when using a schema-compatible theme.
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 WooCommerce

  1. In the WordPress dashboard, go to Products → select a product → scroll to the Product data meta box.
  2. Click the Attributes tab. Add global or custom attributes (e.g., 'Weight', 'Material', 'Dimensions'). Check 'Visible on the product page' for each.
  3. WooCommerce automatically renders these as a 'Additional information' table on the product page using the woocommerce_product_additional_information template.
  4. To rename the section or change styling, go to Appearance → Theme Editor and locate woocommerce/single-product/tabs/additional-information.php (or create a child-theme override at yourtheme/woocommerce/single-product/tabs/additional-information.php).
  5. For richer spec tables with custom layouts, install a plugin such as WooCommerce Product Table (Barn2), YITH WooCommerce Product Add-Ons, or Specs & Info Tables for WooCommerce.
  6. For bulk attribute import, use WP All Import with the WooCommerce add-on, or the built-in WooCommerce Product CSV importer (Products → Import) which supports attribute columns.
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 WooCommerce

  1. In WordPress admin, go to Appearance → Theme File Editor (or use a child theme via FTP/SFTP).
  2. Locate the single product template: woocommerce/templates/single-product/price.php (or the override in your theme at yourtheme/woocommerce/single-product/price.php).
  3. Confirm the price is rendered using WooCommerce's PHP function woocommerce_template_single_price() or the $product->get_price_html() method — these are server-side and output price in the initial HTML.
  4. If a plugin (e.g. a dynamic pricing or currency-switcher plugin) is replacing the price via JavaScript/AJAX, configure it to use server-side rendering or find an alternative that outputs price in HTML.
  5. Install the free 'Schema & Structured Data for WP & AMP' plugin or use Yoast SEO / Rank Math to ensure Product JSON-LD with 'offers.price' is present in the page source.
  6. Verify by visiting a product page, right-clicking → View Page Source, and searching for the price value and 'schema.org/Product'.
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 WooCommerce

  1. Go to WordPress Admin → Products → click the product name to open the edit screen.
  2. In the right-hand sidebar, find 'Product image' — click 'Set product image' to upload or select the main (featured) image.
  3. Below that, find 'Product gallery' — click 'Add product gallery images' to upload all additional angle/detail/lifestyle shots.
  4. In the WordPress media library upload dialog, fill in the 'Alt Text' field for each image before inserting.
  5. Click 'Update' to save the product. The theme will display gallery images in a slider or grid automatically.
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 WooCommerce

  1. Install the Yoast SEO or Rank Math plugin if not already active (Plugins → Add New → search and install).
  2. Go to Products → All Products → [select your product] → Edit.
  3. Scroll down to the 'Yoast SEO' (or 'Rank Math SEO') meta box below the product description.
  4. Click 'Edit snippet' and update the 'SEO title' field with your optimised title.
  5. Click 'Update' to save the product. The plugin writes the <title> tag with your custom value.
  6. For sitewide product title templates, go to Yoast SEO → Search Appearance → WooCommerce → set a template such as %%title%% - %%pa_colour%% | %%sitename%%.
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 WooCommerce

  1. If you use Yoast SEO: Go to WordPress Admin → SEO → General → Features → enable XML Sitemaps, then go to SEO → Search Appearance → Taxonomies and ensure product attribute pages (pa_color, pa_size, etc.) are set to 'noindex' so their URLs don't leak into the sitemap.
  2. Go to SEO → Tools → Bulk editor or SEO → XML Sitemaps → check the sitemap for any ?attribute_pa_* or ?variation_id= URLs and confirm they are excluded. Yoast excludes query-string URLs from its sitemaps by default — if they appear, a custom filter is overriding this.
  3. If you use Rank Math: Go to Rank Math → Sitemap Settings → Products → ensure 'Include Product Variations' is toggled OFF.
  4. If you use All in One SEO (AIOSEO): Go to All in One SEO → Sitemaps → Additional Pages — remove any manually added variant URLs.
  5. Add a canonical tag to variation pages: WooCommerce renders variation state via JavaScript without changing the URL in most themes, but if your theme or a plugin generates unique URLs for variations, use a snippet in functions.php or a plugin like Rank Math to enforce the canonical pointing to the parent product URL.
  6. Regenerate the sitemap (Yoast: deactivate/reactivate XML Sitemaps toggle; Rank Math: flush sitemap cache in Rank Math → Status & Tools → Database Tools) and resubmit 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 WooCommerce

  1. Install the free Yoast SEO or Rank Math SEO plugin if not already present (Plugins → Add New, search for 'Yoast SEO' or 'Rank Math').
  2. WooCommerce variable products generate URLs like /products/my-shirt/?attribute_pa_color=red. Yoast SEO and Rank Math both automatically canonicalise attribute/variation URLs back to the base product URL — confirm this is active.
  3. In Yoast SEO: go to SEO → Search Appearance → WooCommerce and ensure 'Product Variations' is set to 'noindex' or that canonicals are handled (Yoast handles this transparently).
  4. In Rank Math: go to Rank Math → Titles & Meta → WooCommerce and verify 'Canonical URL' is enabled for product pages.
  5. If you have a custom or headless WooCommerce setup, add the canonical programmatically in your theme's functions.php: add_action('wp_head', function(){ if(is_product()){ global $post; echo '<link rel="canonical" href="'.get_permalink($post->ID).'">'; } });
  6. Test with Google Search Console URL Inspection on a ?attribute_ 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 WooCommerce

  1. Install a performance plugin: go to WP Admin → Plugins → Add New and install 'WP Rocket' (paid, most comprehensive) or the free combination of 'LiteSpeed Cache' / 'W3 Total Cache' + 'Autoptimize'. These handle minification, caching, lazy load, and CDN integration in one place.
  2. Images: Install 'Imagify', 'ShortPixel', or 'Smush' (Plugins → Add New) to bulk-compress existing images and auto-compress new uploads. Enable WebP conversion and lazy loading within the plugin settings.
  3. Minify CSS/JS: In your chosen cache plugin's settings, enable 'Minify CSS', 'Minify JS', and 'Defer JS'. Exclude WooCommerce checkout scripts from deferral to avoid breaking the cart.
  4. Enable full-page caching in your cache plugin, and configure cache exclusions for cart, checkout, and my-account pages (most plugins do this automatically for WooCommerce).
  5. CDN: Connect a CDN such as Cloudflare (free tier available) or BunnyCDN through your cache plugin's CDN settings tab to serve static assets from edge locations.
  6. Hosting: Migrate to managed WordPress hosting (e.g. Kinsta, WP Engine, Cloudways) if your current shared host shows high TTFB. Enable PHP 8.x and object caching (Redis/Memcached) in your host's control panel.
Thin contentModerate effort

Expand thin pages to at least 300 words of unique, helpful content that genuinely describes your products, category, or topic.

On WooCommerce

  1. Product pages: WP Admin → Products → [select product] → use the 'Long Description' editor (the large block editor / classic editor panel at the top of the edit screen) for the main expanded content. The 'Short Description' field (right column) appears near the Add-to-Cart button — fill both.
  2. Category pages: WP Admin → Products → Categories → [select category] → fill in the 'Description' field. Install the plugin 'WooCommerce Category Archive Description' or use your theme's built-in support to ensure this text renders on the storefront.
  3. For tag/attribute/filter pages generating thin duplicates, install Yoast SEO or Rank Math → go to SEO → Search Appearance → Taxonomies and set product tags or attributes to 'noindex' if they carry no unique content.
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 WooCommerce

  1. Install the free Yoast SEO or Rank Math plugin if not already active (Plugins → Add New → search name → Install → Activate).
  2. For a product: go to Products → All Products → select the product → scroll to the 'Yoast SEO' (or 'Rank Math SEO') meta box → click the 'Google preview' snippet editor → edit the 'SEO Title' field; the colour-coded bar turns green under 60 characters.
  3. For a category page: go to Products → Categories → hover over the category → click Edit → scroll to the same SEO meta box.
  4. For other pages/posts: Pages → All Pages → select page → same meta box.
  5. To bulk-update titles, use Yoast SEO Premium's bulk editor (SEO → Tools → Bulk editor) or Rank Math's built-in bulk editing table.
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 WooCommerce

  1. Install the free Yoast SEO or Rank Math plugin if not already active (Plugins → Add New).
  2. Navigate to the product, page, or post you want to edit (Products → All Products, or Pages, or Posts).
  3. Open the item in the editor and scroll down to the Yoast SEO / Rank Math meta box.
  4. Click on the 'SEO title' field inside that meta box and type your new 50–60 character title. The on-screen snippet preview will update in real time.
  5. Click 'Update' to save the post/product. The title tag is updated 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 WooCommerce

  1. Install the free 'Yoast SEO' or 'Rank Math SEO' plugin if not already active.
  2. For Yoast SEO: go to SEO → Search Appearance → Content Types → Posts, and ensure 'Show posts in search results' is on; then under each post's Yoast sidebar panel, the author is pulled from the WordPress post author automatically.
  3. To enrich the author entity, go to Users → (Author's profile) and fill in the 'Biographical Info' and the 'Website' field; Yoast will include these in the Person schema.
  4. For Rank Math: go to Rank Math → Titles & Meta → Posts, set 'Structured Data Type' to 'Article' or 'BlogPosting'; author data is pulled from the WordPress user profile automatically.
  5. To add sameAs links for authors, use Rank Math's 'Local SEO' module or a dedicated plugin like 'Schema Pro', which lets you map author social profiles to sameAs.
  6. Verify with Google's Rich Results Test on a live post URL.
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 WooCommerce

  1. The WooCommerce core outputs product structured data via woocommerce/includes/classes/class-wc-structured-data.php.
  2. Do NOT edit core files. Instead, add a filter in your child theme's functions.php or a custom plugin: use the 'woocommerce_structured_data_product' filter to intercept and correct the $markup array.
  3. In your filter callback, check $markup['aggregateRating']['ratingValue']; if it is below 1, either remove the aggregateRating key entirely or adjust worstRating to 0.
  4. Alternatively, install the 'Rank Math SEO' or 'Yoast SEO' plugin — both override WooCommerce's default schema output and have built-in validation for rating bounds.
  5. Flush permalinks (Settings > Permalinks > Save Changes) after any schema changes, 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 WooCommerce

  1. Edit the target page or product in WordPress Admin → Pages / Products → Edit.
  2. In the block editor (Gutenberg), add a Paragraph block at the very top of the content area with your concise direct-answer summary.
  3. Use Heading blocks (H2/H3) formatted as questions for each section.
  4. For FAQ schema: install the free 'Rank Math SEO' or 'Yoast SEO' plugin; both include a dedicated FAQ block you can insert into any page — it renders the visible Q&A and automatically outputs FAQPage JSON-LD.
  5. In Rank Math: add the FAQ Block from the block inserter, fill in questions and answers, and the plugin handles schema output automatically.
  6. Confirm output via 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 WooCommerce

  1. Go to WooCommerce → Settings → Products → Reviews and ensure 'Enable product reviews' is checked and 'Only logged-in customers can leave reviews' is set appropriately.
  2. Install a review-request plugin such as 'ReviewX', 'Cusrev', or 'YITH WooCommerce Advanced Reviews' from WordPress.org Plugins to automate post-purchase review emails.
  3. Configure the plugin to send a review-request email 7–14 days after order completion, targeting products with the lowest review counts.
  4. WooCommerce's built-in schema (via WooCommerce core or Yoast SEO / Rank Math) automatically updates aggregateRating reviewCount as reviews are approved — verify this under Products → [Product] → Reviews tab.
  5. Use Google's Rich Results Test on the product URL once 5 reviews are live to confirm the rich result is valid.
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 WooCommerce

  1. Install and activate the free 'Yoast SEO' plugin (or 'Rank Math SEO') from WordPress.org › Plugins › Add New.
  2. Yoast SEO automatically outputs Article schema on posts and BreadcrumbList schema sitewide once breadcrumbs are enabled: go to Yoast SEO › Search Appearance › Breadcrumbs tab › Enable Breadcrumbs.
  3. To set the correct Article sub-type (Article, BlogPosting, NewsArticle), edit each post and use the Yoast SEO sidebar › Schema tab › Page type / Article type dropdowns.
  4. If you prefer manual control, add a custom plugin or use the theme's functions.php to inject JSON-LD via the wp_head action hook, outputting dynamic values via WordPress template tags (get_the_title(), get_the_author(), get_the_date('Y-m-d')).
  5. Validate the output at any URL using 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 WooCommerce

  1. Install the free Yoast SEO or Rank Math SEO plugin from Plugins → Add New in your WordPress admin if not already installed.
  2. For Yoast SEO: go to SEO → Search Appearance → Breadcrumbs tab, enable breadcrumbs, configure the separator and homepage label, then enable the Yoast structured data feature (it automatically outputs BreadcrumbList JSON-LD on shop, category, and product pages).
  3. For Rank Math: go to Rank Math → Titles & Meta → Products; structured data including BreadcrumbList is output automatically once Rank Math's Schema module is active.
  4. If you prefer manual control, add the JSON-LD block to your child theme's 'single-product.php' or 'archive.php' templates using wp_head() hooks and PHP to dynamically build the breadcrumb path from WooCommerce term data.
  5. Validate each page type (shop, category, product) with 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 WooCommerce

  1. Install the free 'Rank Math SEO' or 'Yoast SEO' plugin (both have dedicated FAQ schema blocks).
  2. With Rank Math: edit any page/post in the block editor, open the Rank Math sidebar panel › Schema › Add New Schema › FAQ. Map each Q&A block automatically.
  3. With Yoast SEO Premium: use the FAQ block available in the Gutenberg block inserter ('Yoast FAQ block') — Yoast automatically wraps it in FAQPage JSON-LD.
  4. For BreadcrumbList: in Rank Math go to Rank Math › General Settings › Breadcrumbs and enable breadcrumbs; in Yoast go to SEO › Search Appearance › Breadcrumbs and enable them, then add <?php yoast_breadcrumb(); ?> to your theme template.
  5. For manual JSON-LD without a plugin: add the script block to your child theme's functions.php using wp_head action hook, or insert it via a plugin like 'Insert Headers and Footers'.
  6. 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 WooCommerce

  1. Install and activate a schema plugin such as 'Rank Math SEO', 'Yoast SEO' (with its Schema tab), or 'Schema & Structured Data for WP & AMP'.
  2. For Rank Math: edit the instructional page in WordPress, open the Rank Math sidebar › Schema › Add New Schema › select HowTo; fill in steps using the guided UI, which auto-generates the JSON-LD.
  3. For manual injection without a plugin: edit your child theme's functions.php and use wp_head action hook to echo a <script type='application/ld+json'> block conditionally (e.g. is_page('how-to-slug')).
  4. BreadcrumbList is automatically output by Rank Math and Yoast on all pages when breadcrumbs are enabled under their settings.
  5. 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 WooCommerce

  1. Install and activate the free 'Rank Math SEO' or 'Yoast SEO' plugin from WordPress.org plugins.
  2. In Rank Math: go to Rank Math → Titles & Meta → Local SEO tab, enable Local SEO, fill in business name, address, phone, and hours — Rank Math outputs the JSON-LD automatically.
  3. In Yoast SEO: go to Yoast SEO → Search Appearance → Company info and fill in the details; Yoast injects Organisation/LocalBusiness schema sitewide.
  4. Alternatively, add the JSON-LD block manually via Appearance → Theme Editor → header.php (child theme only) just before </head>, or use a plugin like 'Insert Headers and Footers' to paste the script without editing PHP.
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 WooCommerce

  1. Install and activate the free 'Rank Math SEO' or 'Yoast SEO' plugin from Plugins → Add New.
  2. For Rank Math: go to Rank Math → Titles & Meta → Local SEO. Enable 'Local SEO' and fill in your organization name, logo, and social profiles — Rank Math outputs Organization schema automatically.
  3. For Yoast SEO: go to Yoast SEO → Settings → Site representation. Choose 'Organization', enter your name and logo, and add social URLs under 'Social profiles'.
  4. Alternatively, add the JSON-LD block manually via Appearance → Theme File Editor → header.php (or your child theme's functions.php using wp_head hook), pasting the script tag before </head>.
  5. 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 WooCommerce

  1. Install and activate a structured data plugin — the most widely used free option is 'Rank Math SEO' or 'Yoast SEO'; both auto-generate Product schema on WooCommerce product pages when WooCommerce is detected.
  2. For Rank Math: go to Rank Math → Titles & Meta → WooCommerce → verify 'Schema Type' is set to 'Product'. Rank Math will pull price, availability, and rating data automatically.
  3. For Yoast SEO Premium (or Yoast WooCommerce SEO plugin): Settings → Yoast SEO → Schema → confirm product pages output 'Product' schema type.
  4. If you prefer manual control: add a code snippet via a plugin like 'Code Snippets' that uses the woocommerce_single_product_summary action hook to echo a <script type='application/ld+json'> block, using WooCommerce's $product object to populate fields dynamically.
  5. Validate a product page URL with Google's Rich Results Test to confirm Product schema is present and error-free.
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 WooCommerce

  1. Install and activate the free Yoast SEO plugin (or RankMath SEO) — both automatically output Organization and WebPage schema on every page.
  2. For Yoast: go to Yoast SEO → Settings → Site representation. Set 'Your site represents' to 'An organization', fill in Organization name, logo, and social profiles. Yoast then outputs Organization schema sitewide and WebPage schema on each page automatically.
  3. For RankMath: go to RankMath → Titles & Meta → Global Meta. Under 'Schema Type' choose 'WebPage'. Then go to RankMath → General Settings → Webmaster Tools / Local SEO to set up Organization details.
  4. To add or customize schema manually, place a JSON-LD <script> block inside your theme's header.php (child theme recommended) using wp_head() hook, or use the 'Insert Headers and Footers' plugin to paste the code without editing PHP.
  5. 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 WooCommerce

  1. Install and activate the 'Yoast SEO' or 'Rank Math SEO' plugin if not already present — both output ISO-8601 dates automatically for most schema types.
  2. If you have custom or theme-generated JSON-LD, go to Appearance → Theme File Editor (or use a child theme) and locate the file outputting structured data — often 'functions.php', 'single-product.php', or a dedicated schema file.
  3. Find any PHP date output and replace informal formats with: date('Y-m-d\TH:i:s\Z', strtotime($date_string)) or use WordPress's built-in: get_the_date('Y-m-d\TH:i:s\Z').
  4. For Yoast SEO: Settings are mostly automatic; verify under SEO → Search Appearance → Content Types that the correct post/product types are enabled for schema output.
  5. For Rank Math: Go to Rank Math → Titles & Meta → Posts/Products and confirm Schema type is set; Rank Math handles ISO formatting natively.
  6. Test any updated product or post URL in Google's Rich Results Test to confirm dates pass without warnings.
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 WooCommerce

  1. Install the free Yoast SEO or Rank Math plugin if not already active.
  2. For Yoast SEO: go to Yoast SEO → Settings → Site representation; fill in your organization name, logo, and then scroll to 'Other profiles' to add all your social/profile URLs — Yoast automatically outputs these as `sameAs` in JSON-LD.
  3. For Rank Math: go to Rank Math → Titles & Meta → Local SEO → Social Profiles; enter each URL; Rank Math injects them into the Organization schema site-wide.
  4. If you prefer manual control, add the JSON-LD snippet via your child theme's `functions.php` using `wp_head` action hook, or use the 'Insert Headers and Footers' plugin to paste the script into the <head>.
  5. Verify the output 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 WooCommerce

  1. In WordPress admin, go to Pages / Products and confirm the post status is 'Published' and visibility is 'Public' (not 'Password protected' or 'Private').
  2. Check your Yoast SEO or Rank Math plugin settings: SEO → Search Appearance → confirm the post type is not set to 'noindex'. Also check individual page meta under the SEO tab on each page.
  3. Review Settings → Reading — ensure 'Discourage search engines from indexing this site' is NOT checked (this sets a sitewide noindex/robots block).
  4. For redirect management, use the Redirection plugin (Tools → Redirection) to create 301 redirects from broken URLs to correct live pages.
  5. To add or fix structured data, install Yoast SEO (includes Product and breadcrumb schema automatically for WooCommerce), or use a dedicated plugin like 'Schema Pro' or 'Rank Math' for FAQPage and additional schema types.
  6. After fixing, submit 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 WooCommerce

  1. In your WordPress Admin go to Appearance → Theme File Editor (or use a code editor via FTP/SSH to the theme directory for safety).
  2. Search your active theme's template files (`single-product.php`, `cart.php`, page templates, and any block patterns) and your child theme's `functions.php` for the offending `aria-` attribute.
  3. Edit the relevant template or partial file, remove or correct the disallowed ARIA attribute, and save.
  4. If the violation comes from a plugin (e.g. a slider, tabs, or filter plugin), check the plugin's settings for an option to disable or customise its markup; if none exists, report it to the plugin author or replace the plugin.
  5. Use the axe DevTools browser extension or WP Accessibility Helper plugin to re-scan the page after saving.
  6. Clear any caching plugin (e.g. WP Rocket, W3 Total Cache) and re-test on the live site.
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 WooCommerce

  1. In your WordPress admin go to Appearance → Theme File Editor (or use a child theme and edit files via FTP/SFTP).
  2. Search your active theme's template files (e.g., `header.php`, `functions.php`, page templates, or WooCommerce template overrides in `/woocommerce/` folder) for the disallowed role attribute.
  3. Remove or correct the role attribute in the relevant template file.
  4. If the element comes from a plugin (e.g., a page builder widget), check that plugin's block/widget settings for a custom HTML or CSS class field where a role may have been manually added.
  5. Save and verify with axe DevTools or Lighthouse in Chrome.
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 WooCommerce

  1. In WordPress admin, go to Appearance → Theme File Editor (or use a child theme / local editor).
  2. Locate the template file that renders the unlabelled element — common culprits are header.php, functions.php template parts, or WooCommerce template overrides in woocommerce/templates/.
  3. Add `aria-label="Descriptive action"` to the offending `<button>` or `<a>` tag. For SVG icons, add `aria-hidden="true"` to the SVG and a visually-hidden `<span class="screen-reader-text">Label</span>` inside the button (WordPress core ships with the .screen-reader-text CSS class).
  4. Alternatively, install the free 'WP Accessibility' plugin which can patch common missing labels without touching code.
  5. Save changes and verify with the axe browser extension on the front end.
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 WooCommerce

  1. WooCommerce dialogs are typically rendered by your WordPress theme or a page-builder plugin.
  2. For theme-provided modals (e.g. cart drawer, quick-view), go to Appearance → Theme File Editor (or edit via FTP/SFTP) and locate the relevant template file — often woocommerce/cart/mini-cart.php, or a partial in your child theme.
  3. Find the dialog's container element and add aria-labelledby pointing to the heading's id, or add aria-label with a descriptive name.
  4. If using a plugin like WooCommerce Quick View or CartFlows, check the plugin's settings for accessibility options first; if none exist, use a child theme or a small Code Snippets plugin (wp_footer hook) to patch the attribute via JavaScript after the DOM loads.
  5. Install the 'axe DevTools' browser extension, load a page with the dialog, open it, and confirm the name is announced.
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 WooCommerce

  1. In WordPress Admin, go to Appearance → Theme File Editor (or use a local/staging environment with a code editor for safety).
  2. Open your active theme's template files — particularly `header.php`, `footer.php`, `functions.php`, and WooCommerce template overrides in `yourtheme/woocommerce/` — and search for `aria-hidden`.
  3. If you use a page builder (Elementor, Divi, Beaver Builder), switch to the page builder editor for the affected page and inspect the widget/module settings; many builders have an 'Accessibility' or 'Advanced' panel where `aria-hidden` may be set.
  4. Install the free 'axe Accessibility Linter' or run Lighthouse in Chrome DevTools to pinpoint the exact element and template file.
  5. Edit the markup to remove `aria-hidden` from parent containers that wrap interactive elements, then update the theme or create a child-theme override so updates don't revert your fix.
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 WooCommerce

  1. Go to your WordPress Dashboard → Appearance → Theme File Editor (or use a code editor via FTP/SFTP).
  2. Locate the template file containing the problematic input — common locations are woocommerce/templates/global/form-row.php, templates/checkout/form-checkout.php, or a widget template.
  3. To override a WooCommerce template safely, copy it to your child theme under yourtheme/woocommerce/... preserving the folder structure, then edit the copy.
  4. In the template file, add a <label for="field-id">Descriptive name</label> before the <input>, or add aria-label="Descriptive name" directly on the <input> element.
  5. For theme-level custom inputs (e.g., in a page builder widget), use your child theme's functions.php with a woocommerce_form_field filter to inject label attributes programmatically.
  6. Install and run the free 'WP Accessibility' plugin (by Joe Dolson) as a supplement — it patches common labeling issues in WordPress core forms automatically.
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 WooCommerce

  1. In WordPress Admin, go to Appearance → Theme File Editor (or use a child theme / FTP) and search for `role="meter"`, `<meter`, or star-rating templates in your active theme.
  2. WooCommerce's built-in star rating is rendered in `woocommerce/templates/single-product/rating.php` and `loop/rating.php`. Copy these to your child theme under `yourchildtheme/woocommerce/single-product/rating.php`.
  3. Open the copied file and add `aria-label="Average customer rating"` to the `<meter>` or `role="meter"` element.
  4. Alternatively, install the 'WP Accessibility' plugin (by Joe Dolson) which patches several common ARIA labelling issues automatically, then verify the meter fix specifically.
  5. Clear any caching plugins and test with axe DevTools or a screen reader.
Aria progressbar nameQuick win

Add a descriptive accessible name to every progress bar element so screen readers can announce what it represents.

On WooCommerce

  1. Progress bars in WooCommerce typically come from the active theme or plugins (e.g., a checkout-steps plugin, password-strength meter from woocommerce-password-strength-meter.js, or a loyalty plugin).
  2. For theme-rendered bars: in your WordPress admin go to Appearance → Theme File Editor (or edit via FTP/SSH) and open the relevant template partial (e.g., `form-login.php`, `checkout/form-checkout.php`). Add `aria-label="…"` to the element.
  3. For WooCommerce's built-in password strength meter (rendered by `woocommerce/assets/js/frontend/password-strength-meter.js`): add a filter in your child theme's `functions.php` or a custom plugin to enqueue a small JS snippet that sets `aria-label` on `#password-strength` after DOM-ready.
  4. For plugin-generated bars, check the plugin's settings for an accessibility field; if absent, add the attribute via a targeted JS snippet in your child theme's `functions.php` using `wp_add_inline_script`.
  5. Clear any caching plugins (e.g., WP Rocket, W3 Total Cache) and validate with the axe DevTools browser 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 WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a child theme via FTP/SFTP).
  2. Search your active (child) theme's template files for the prohibited attribute — check `header.php`, `functions.php`, page templates, and any WooCommerce template overrides in `/woocommerce/` within your theme folder.
  3. Edit the relevant template file to remove or correct the prohibited ARIA attribute.
  4. If the issue originates from a plugin (e.g., a slider, popup, or mega-menu plugin), check that plugin's settings for an accessibility mode, or update to the latest version, or contact the plugin author.
  5. Use the axe DevTools browser extension to verify the fix on the frontend.
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 WooCommerce

  1. In your WordPress Admin, go to Appearance → Theme File Editor (or use a child theme and edit files via FTP/SFTP to avoid overwriting on updates).
  2. Search your theme's PHP/HTML template files (e.g., `header.php`, `page.php`, `woocommerce/` override templates) for `role=` attributes.
  3. Add the missing required attribute directly in the template markup, e.g., `<div role="heading" aria-level="2">`.
  4. If the violation is inside a plugin's output (e.g., a slider or product filter), check that plugin's settings for accessibility options, or open a support ticket with the plugin author.
  5. Install the free 'axe Accessibility Linter' browser extension and re-check the page to verify the fix.
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 WooCommerce

  1. The violation is almost always in your active WordPress theme's template files or a page-builder block. Go to Appearance → Theme File Editor (or use a child theme via FTP/SFTP).
  2. Search theme files (header.php, functions.php, or relevant template parts in /template-parts/) for role= attributes. Look in navigation menus, product loops, or slider shortcodes.
  3. To edit safely, create or use a child theme: copy the relevant parent template file into your child theme folder, then edit it there so updates don't overwrite your fix.
  4. If the issue is inside a Gutenberg block or page-builder (Elementor, Divi, etc.), edit the block/section: remove or replace the offending tabindex attributes, or switch to a semantic HTML element (e.g. change a <div role="list"> to a <ul>).
  5. For plugin-generated markup (e.g. WooCommerce core product grid), use a template override: copy woocommerce/templates/[relevant-template].php to your-theme/woocommerce/[relevant-template].php and correct the markup there.
  6. Re-test with axe DevTools browser extension after saving.
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 WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a child theme via FTP/SFTP).
  2. Find the template responsible for the flagged widget — common locations are `woocommerce/single-product/tabs/tabs.php` (copy this to `your-child-theme/woocommerce/single-product/tabs/tabs.php` to override safely).
  3. Inside the template, wrap the list of tab `<li>` elements (which carry `role="tab"` or similar) in a container with `role="tablist"`.
  4. If the widget comes from a page builder (e.g., Elementor, Divi), edit the widget's HTML/Advanced tab or use a Custom HTML widget to add the correct wrapper role.
  5. After saving, run Axe DevTools or WAVE browser extension to confirm the violation is resolved.
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 WooCommerce

  1. In your WordPress Admin, go to Appearance → Theme File Editor (or use a code editor / FTP client for safer editing).
  2. Search your active theme's template files (e.g., `header.php`, `footer.php`, `archive.php`, page templates, and any custom block templates) for `role=`.
  3. Fix invalid role values directly in the theme files, or better yet, use a Child Theme so your edits survive theme updates (Appearance → Themes → Add New → search 'child theme' plugins like 'Child Theme Configurator').
  4. For role issues inside page-builder-generated content (Elementor, Divi, Beaver Builder), open the specific element in that builder's editor, locate its 'Advanced' or 'Accessibility' panel, and correct the role field.
  5. For role issues in plugins (e.g., a slider, mega-menu, or modal plugin), check the plugin settings for ARIA/accessibility options, or open a support ticket with the plugin author.
  6. Save and re-test with the axe DevTools or WAVE browser extension.
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 WooCommerce

  1. In WordPress admin, go to Appearance → Theme File Editor (or use a child theme / FTP to avoid overwriting on update).
  2. Locate the template file containing the toggle: for checkout checkboxes it is typically woocommerce/checkout/form-checkout.php or a plugin template override in your theme's woocommerce/ folder.
  3. Add a <label for='field-id'> paired with an id attribute on the <input>, or use aria-label on any custom toggle widget.
  4. If the toggle is output by a plugin (e.g. a cookie consent or newsletter opt-in plugin), check that plugin's settings for a 'label text' field, or use a CSS + JS snippet in a child theme's functions.php to inject aria-label.
  5. Install the WP Accessibility plugin (by Joe Dolson) for additional helpers, then re-run an axe scan to verify.
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 WooCommerce

  1. Log in to WordPress Admin. If tooltips are added by your theme, go to Appearance → Theme File Editor and search for 'role="tooltip"' in your child theme's template files.
  2. If tooltips come from a plugin (e.g., YITH WooCommerce Wishlist, WooCommerce Product Add-Ons), navigate to that plugin's settings page and look for tooltip label or text fields.
  3. For theme-based tooltips, edit the relevant PHP template (e.g., woocommerce/content-product.php or a custom template part) and add aria-label="[descriptive text]" to the tooltip element.
  4. If you use a page builder (Elementor, Divi, Beaver Builder), open the widget/module that generates the tooltip and fill in the 'Tooltip text' or 'Accessible label' field — or use the Advanced → Custom Attributes panel to add aria-label.
  5. Install and run the axe DevTools browser extension to confirm the fix, then re-run your accessibility scanner.
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 WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a local code editor / SFTP client for safer editing).
  2. Search your active theme's template files (`header.php`, `footer.php`, page templates, WooCommerce template overrides in `/woocommerce/`) for the misspelled ARIA attribute.
  3. If found in the theme, correct the spelling and save.
  4. If the error originates in a plugin (e.g. a page builder, form plugin, or slider), go to Plugins → Installed Plugins, check for updates, or open the plugin's support forum to report the bug.
  5. For block-theme users, open Appearance → Editor and inspect any custom HTML blocks for misspelled ARIA attributes.
  6. Verify with the axe browser extension after saving.
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 WooCommerce

  1. In WordPress admin, go to Appearance → Theme File Editor (or use a child theme and a code editor like VS Code via FTP/SFTP).
  2. Search your active theme's template files (especially woocommerce/ overrides in your child theme, header.php, footer.php, and any custom page builders' output) for ARIA attributes.
  3. Correct any ID-reference mismatches or invalid keyword values directly in the template PHP/HTML files.
  4. If the issue is in a plugin's output (e.g., a slider or modal plugin), check the plugin's settings for ARIA options; if none exist, open a support ticket with the plugin author or override their template in your child theme.
  5. Install the free axe DevTools browser extension, load the affected page, run the scan, and confirm all aria-valid-attr-value violations are gone.
  6. Flush any caching plugin (e.g., WP Super Cache, W3 Total Cache) after saving so the updated markup is served.
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 WooCommerce

  1. WooCommerce's default checkout and My Account forms are output by WooCommerce templates and already include autocomplete attributes, but themes or plugins may override them.
  2. To override safely, copy the relevant template from wp-content/plugins/woocommerce/templates/ (e.g., checkout/form-billing.php, checkout/form-shipping.php) into wp-content/themes/your-theme/woocommerce/ preserving the folder structure.
  3. Open the copied file and locate each <input> element; add or correct the autocomplete attribute using the correct HTML token (e.g., autocomplete='given-name').
  4. Alternatively, use the woocommerce_checkout_fields filter in your child theme's functions.php or a custom plugin to programmatically inject the autocomplete value into the field's attributes array.
  5. Save, clear any caching plugins, and verify with axe DevTools.
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 WooCommerce

  1. In wp-admin, go to Appearance → Theme File Editor (or use a local code editor / FTP).
  2. Search your active theme's template files (`single-product.php`, `archive-product.php`, page templates, etc.) for `style=` containing the flagged spacing properties.
  3. Remove the inline property and add or extend a CSS class on that element.
  4. Add the corresponding CSS rule to your child theme's `style.css` file (never edit the parent theme directly).
  5. If a page builder (Elementor, Divi, Beaver Builder) generated the inline style, open that block/widget, find the Typography or Spacing panel, delete the custom spacing value, and save — the builder will stop emitting the inline style.
  6. Verify via Appearance → Customize → Additional CSS or the axe DevTools browser extension.
Button nameModerate effort

Add a visible or programmatically accessible name to every button so screen readers can announce what it does.

On WooCommerce

  1. In WordPress Admin go to Appearance → Theme File Editor (or use a child theme via FTP/SFTP).
  2. Open the relevant template file — often header.php, woocommerce/cart/mini-cart.php, or a page-builder block — and find the <button> element.
  3. Add aria-label="Descriptive name" to the button tag, or insert <span class="screen-reader-text">Descriptive name</span> inside it — WooCommerce and most WordPress themes already define a .screen-reader-text CSS class.
  4. If you use a page builder (Elementor, Divi, etc.), switch to the HTML/code block for that button widget and add the aria-label attribute there.
  5. Install the free 'WP Accessibility' plugin as a helper — it adds screen-reader-text utilities and can audit some common issues automatically.
  6. Verify the fix with the axe browser extension after saving.
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 WooCommerce

  1. In WordPress admin go to Appearance → Customize → Colors (or your theme's color section). If your theme provides a color picker for body text or link color, update it there.
  2. If your theme does not expose the specific color, go to Appearance → Customize → Additional CSS and add an override rule, e.g.: `a { color: #005f8e; }` using a shade that passes 4.5:1.
  3. For block themes (Full Site Editing), go to Appearance → Editor → Styles → Edit Styles → Colors and update the global palette entry for the failing color.
  4. If you use a page builder (Elementor, Divi, etc.), open its Global Settings / Theme Builder and update the color token in the global color palette.
  5. Save, then use Chrome DevTools (F12) → Lighthouse → Accessibility to verify no contrast failures remain.
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 WooCommerce

  1. Product specification/attribute lists are often output by WooCommerce core as `<table>` elements, but custom themes or page builders may use `<dl>`. Identify the source by inspecting the page HTML in your browser (right-click → Inspect).
  2. If the `<dl>` is in a theme template file, locate it under Appearance → Theme File Editor (or via FTP/SFTP in your theme folder). Common files: 'woocommerce/single-product/tabs/additional-information.php' or a custom snippet.
  3. Edit the PHP/HTML template to ensure the `<dl>` contains only `<dt>` and `<dd>` children. Use child-theme overrides (place the file in your child theme's 'woocommerce/' folder) so updates don't overwrite your changes.
  4. If the `<dl>` originates from a page-builder block (Elementor, Divi, Beaver Builder), open the block editor, switch to the HTML/Code view of that element, and correct the markup directly.
  5. Save, clear any caching plugins (e.g., WP Rocket, W3 Total Cache), and validate with axe DevTools or WAVE.
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 WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a child theme and edit via FTP/SFTP).
  2. Search for `<dt` or `<dd` inside your active theme's template files. Common culprits are `woocommerce/templates/single-product/product-attributes.php` or a copied override in `yourtheme/woocommerce/single-product/product-attributes.php`.
  3. To safely override the WooCommerce core template without losing changes on plugin updates, copy `wp-content/plugins/woocommerce/templates/single-product/product-attributes.php` to `wp-content/themes/your-child-theme/woocommerce/single-product/product-attributes.php`.
  4. Open the copied file and confirm the `<dt>` and `<dd>` elements are children of a `<table>` or `<dl>`. If they are orphaned, wrap them in `<dl>...</dl>`.
  5. Save the file and use the axe DevTools browser extension on the product page to verify the fix.
Document titleQuick win

Add a unique, descriptive <title> element to every page so browsers, screen readers, and search engines can identify it.

On WooCommerce

  1. Install and activate the free Yoast SEO or RankMath plugin if not already present — these give you per-page title control without touching code.
  2. For individual products: Products → [select product] → scroll to the Yoast SEO / RankMath meta box → 'SEO Title' field → enter your title.
  3. For category pages: Products → Categories → [select category] → Yoast SEO / RankMath panel → 'SEO Title'.
  4. For global title templates (so all products auto-generate titles): In Yoast SEO go to SEO → Search Appearance → WooCommerce tab → set the title template using variables like %%title%% %%sep%% %%sitename%%. In RankMath: RankMath → Titles & Meta → Products.
  5. To fix the raw theme title tag: Appearance → Theme File Editor → open your active theme's header.php and confirm <title> is not hardcoded — it should use wp_title() or the SEO plugin's output hook.
  6. Verify by visiting any product page and checking the browser tab.
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 WooCommerce

  1. In your WordPress dashboard, go to Appearance → Theme File Editor (or use a child theme) and search your template files (e.g. single-product.php, archive.php) for `<h1>`, `<h2>`, `<h3>` tags with no inner text.
  2. For pages built with the Block Editor (Gutenberg), open the page/post, look for Heading blocks that are empty, and either type content into them or delete the block.
  3. For pages built with Elementor: open the page in Elementor, switch to the Navigator panel (View → Navigator), look for Heading widgets with no text in the Content → Title field, add text or delete the widget.
  4. For pages built with Divi: open the page in Divi Builder, find Heading modules with an empty 'Heading Text' field, fill them in or remove the module.
  5. Install the 'WP Accessibility' plugin (by Joe Dolson) which includes a heading-structure checker under Tools → Accessibility to help locate remaining issues.
  6. Re-test with the WAVE browser extension.
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 WooCommerce

  1. Empty table headers in WooCommerce most commonly appear in product attribute tables, variation tables, or shortcode-generated content in page builders.
  2. For theme template files: in your WordPress admin go to Appearance → Theme File Editor (or use an FTP client). Locate templates such as `single-product/tabs/additional-information.php` or `loop/price.php` in your active (child) theme.
  3. Search those files for `<th` with no inner content and add descriptive text or `aria-label` attributes.
  4. For content inside the WordPress block editor or classic editor: open the Product or Page, switch to the 'Code editor' view (three-dot menu → Code editor in the block editor, or the 'Text' tab in the classic editor), find the `<th>` tags, and add labels.
  5. Use the 'WP Accessibility' plugin (by Joe Dolson) to perform an ongoing site-wide accessibility scan.
  6. Re-test with the WAVE browser extension or axe DevTools after saving.
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 WooCommerce

  1. For iframes in page/post content: go to WordPress Admin → Pages (or Posts) → Edit the page → switch to the 'Code editor' view (block editor top-right menu) or the 'Text' tab (classic editor).
  2. Find the `<iframe>` tag and add `title="Your descriptive label"` to it, then save.
  3. For iframes in your theme template files (e.g. header.php, footer.php, or a custom template): go to Appearance → Theme File Editor (or use an FTP/SSH client), open the relevant PHP file, locate the iframe, and add the title attribute.
  4. For iframes added by plugins (e.g. contact form, map, or payment plugins): check the plugin's settings page for an 'iframe title' or 'accessibility label' field. If none, add a small JavaScript snippet to your child theme's functions.php using wp_footer hook or via a plugin like 'Custom Scripts': `add_action('wp_footer', function(){ echo "<script>document.querySelectorAll('iframe:not([title])').forEach(el => el.title = 'Embedded content');</script>"; });`.
  5. Use the browser inspector to verify each iframe now has a meaningful 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 WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a child theme) and open the relevant template file (e.g., single-product.php, archive-product.php, page.php).
  2. Locate heading tags and correct any that skip levels — change <h3> to <h2> if it follows an <h1>, for example.
  3. For content in the block editor (Gutenberg), edit the page/post, click a Heading block, and use the 'H1/H2/H3…' picker in the block toolbar to set the correct level.
  4. If using a page builder (Elementor, Beaver Builder, Divi), open the page in the builder, click each heading widget, and in its settings change the HTML Tag from H3/H4 to the correct sequential level.
  5. Install the WAVE or axe DevTools browser extension and re-audit the live page to confirm the fix.
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 WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a local code editor / FTP).
  2. Open your active theme's `header.php` file (child theme recommended — never edit a parent theme directly).
  3. Locate the `<html` tag. WordPress themes typically use the `language_attributes()` template tag: `<html <?php language_attributes(); ?>>`. If this is already present, WordPress automatically inserts the correct `lang` attribute based on Settings → General → Site Language.
  4. If the tag is missing `language_attributes()`, add it: `<html <?php language_attributes(); ?>`.
  5. Go to Settings → General and confirm 'Site Language' is set to your store's primary language — this controls what value `language_attributes()` outputs.
  6. For multilingual stores using WPML or Polylang, those plugins automatically switch the `lang` value per translated page when `language_attributes()` is used.
  7. Save changes and verify via View Source.
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 WooCommerce

  1. WordPress automatically adds `lang` to `<html>` via the `language_attributes()` template tag. First check: go to WP Admin → Settings → General and ensure 'Site Language' is set to your desired language (e.g. English (United States)).
  2. If the theme's `header.php` (or root layout) does not include `language_attributes()`, open your child theme's `header.php` (Appearance → Theme File Editor → header.php) and ensure the `<html>` tag reads: `<html <?php language_attributes(); ?>>`.
  3. Save, then view page source and confirm `lang="en-US"` (or your locale) appears on the `<html>` tag.
  4. If a page builder (Elementor, Divi, etc.) overrides the template, check the builder's 'Custom Code' or 'Theme Builder' header template for a hardcoded `<html>` tag and apply the same fix.
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 WooCommerce

  1. **Product images:** WP Admin → Products → select a product → click the featured image or gallery image thumbnail → in the Media Library modal, fill in the 'Alt Text' field on the right panel → click 'Update'.
  2. **Media Library bulk update:** WP Admin → Media → Library → click any image → fill in the 'Alternative Text' field in the right panel.
  3. **For bulk editing:** Install the free plugin 'Bulk Auto Image Alt Tag' or 'SEO Optimized Images' to audit and fill missing alt text across all media at once.
  4. **Hardcoded images in theme files:** In your child theme, locate the template file (e.g., `single-product.php`) and add `alt` attributes directly to any `<img>` tags.
  5. **Page builder images (Elementor/Divi):** Edit the page → click the image widget → find the 'Alt Text' or 'Alternative Text' field in the widget settings panel.
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 WooCommerce

  1. For product images: go to wp-admin → Products → select the product → click the featured image or gallery image → in the WordPress Media Library panel on the right, find the 'Alternative Text' field and clear it (leave empty, not blank space).
  2. For images in page/post content: open the page in the Block Editor (Gutenberg) → click the image block → in the right sidebar under 'Alt text (alternative text)', clear the field; tick 'Mark as decorative' if available in your WP version.
  3. For images in the Classic Editor: click the image → click the pencil (edit) icon → clear the 'Alternative Text' field → Update.
  4. For theme or widget images: go to Appearance → Widgets or Appearance → Customize and find the image widget; clear its alt/title text field.
  5. Install the free 'Accessible Images' or 'WP Accessibility' plugin to bulk-audit and update alt attributes across the media library.
Input button nameQuick win

Add a descriptive label to every input button so screen readers can announce what the button does.

On WooCommerce

  1. Identify which theme file renders the button. Common locations: `woocommerce/templates/cart/proceed-to-checkout-button.php`, `templates/checkout/form-checkout.php`, or a child theme override.
  2. In your WordPress admin, go to Appearance → Theme File Editor (or use an FTP client / file manager), open your child theme folder, and locate the relevant WooCommerce template file.
  3. Find the `<input type="submit"` or `<input type="button"` tag with an empty `value` and add a descriptive label: e.g., `<input type="submit" value="<?php esc_attr_e('Place Order', 'woocommerce'); ?>"`.
  4. If you cannot safely edit template files, install a plugin like 'Code Snippets' or use your child theme's `functions.php` to hook into the output with `woocommerce_order_button_text` filter to supply the label.
  5. Clear any caching plugin cache and re-test the page with an accessibility scanner or screen reader.
LabelModerate effort

Add a visible or programmatic label to every form input so assistive technologies can identify its purpose.

On WooCommerce

  1. Install the 'WP Accessibility' plugin (by Joe Dolson) from Plugins → Add New — it auto-fixes several common label issues including search fields.
  2. For checkout and account forms, copy the relevant WooCommerce template file (e.g., woocommerce/templates/checkout/form-billing.php) into your child theme under woocommerce/checkout/form-billing.php.
  3. Open the copied file in Appearance → Theme File Editor (or via FTP/SFTP) and add <label for="INPUT_ID"> elements above any unlabeled inputs.
  4. For custom or plugin-added forms, use your theme's functions.php or a code snippet plugin to hook into the form output and inject labels.
  5. Use the axe DevTools browser extension to re-scan checkout, login, and registration pages after changes.
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 WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a child theme via FTP/SFTP) and locate the relevant template — common files include `woocommerce/templates/checkout/form-checkout.php`, `form-login.php`, and `form-billing.php`.
  2. Alternatively, install the free 'WooCommerce Accessibility' plugin or use a page builder's field settings to add visible labels.
  3. For fields missing a visible label, override the WooCommerce template in your child theme: copy the file from `wp-content/plugins/woocommerce/templates/` to `wp-content/themes/YOUR-CHILD-THEME/woocommerce/` and edit the input to add `<label for="FIELD_ID">Label Text</label>` before each `<input>`.
  4. For widgets like the search bar: go to Appearance → Widgets, find the Search widget, and ensure the label is visible (many themes hide it with CSS — remove the `display:none` or `visibility:hidden` style from the label rather than deleting the label element itself).
  5. Re-run your accessibility scanner to confirm the fix.
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 WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a child theme via FTP/SFTP to avoid losing changes on theme update).
  2. Open `header.php` in your active (child) theme. This file contains the `<header>` element that renders the banner landmark.
  3. Check that the `<header>` (or element with `role="banner"`) is opened in `header.php` and closed before any `<main>` element opens in `index.php` / `page.php` / `single.php`.
  4. If a plugin or page builder is wrapping the entire page layout (e.g., a `<section>` or `<div role="main">` that starts above the `<header>`), edit or disable that wrapper in the relevant template or plugin settings.
  5. Save changes and verify with axe DevTools or WAVE that the banner landmark is now a top-level region.
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 WooCommerce

  1. Your page template is controlled by your active WordPress theme. Go to Appearance → Theme File Editor (or edit via FTP/SSH) and open the relevant template file — commonly `page.php`, `single-product.php`, or `sidebar.php`.
  2. Search for `<aside`, `get_sidebar()`, or `role="complementary"` within the template. Note which landmark element wraps it (e.g. `<main id="main">`).
  3. If the sidebar is rendered inside `<main>`, restructure the template so `<main>` contains only the primary content and the `<aside>` / `get_sidebar()` call is a sibling outside `<main>`, both wrapped by a layout `<div class="site-content">`.
  4. Update your theme's CSS (`style.css` or a child-theme stylesheet) to apply a flexbox or grid layout to the wrapper div so the sidebar still appears visually alongside the content.
  5. If using a block theme (Full Site Editing), go to Appearance → Editor, select the template, find the Sidebar block, and use the block mover to place it outside the Main Content block at the root level of the template canvas.
  6. Test with the axe DevTools browser extension and a screen reader landmark shortcut to verify the fix.
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 WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a code editor / FTP).
  2. Open your active theme's `footer.php` file. The `<footer>` tag here is typically already outside `<main>` if the theme is well-structured, but confirm.
  3. Also open `header.php` and any template files (e.g., `index.php`, `page.php`, `single.php`) to see how `get_header()`, `get_main()`, and `get_footer()` are ordered — `get_footer()` must always come after the closing `</main>` tag.
  4. If your theme wraps the footer inside a `<main>` or `<article>` in a template file, move the `get_footer()` call (or the `<footer>` block) to appear after the `</main>` closing tag.
  5. Save the file. Use axe DevTools in your browser to confirm the landmark is now top-level.
  6. If using a block theme (Full Site Editing), go to Appearance → Editor → Templates → edit the relevant template, and drag the Footer block outside any Group block that has a 'Main' or other landmark role assigned in block settings.
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 WooCommerce

  1. In WordPress Admin, go to Appearance → Theme File Editor (or use a child theme and edit files via FTP/SSH).
  2. Open `header.php` of your active theme. Check whether the theme opens a `<main>` or `<div role="main">` tag inside the `<header>` block.
  3. Also open `index.php`, `page.php`, `single.php`, and `archive.php` — whichever template closes the main tag — to see the full structure.
  4. Restructure so the `<main>` (or `role="main"` wrapper) opens *after* `get_header()` / the closing `</header>` tag and closes *before* `get_footer()` / the opening `<footer>` tag, making it a sibling of, not a child of, other landmarks.
  5. If your theme uses a page builder like Elementor or Divi, check those builders' global layout/canvas settings for an option to wrap content in `<main>` and ensure it is placed at the top level.
  6. Use the axe browser extension on the front end to confirm the fix.
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 WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a child theme via FTP/SSH).
  2. Open header.php (your active or child theme's file). Search for '<header' and 'role="banner"'.
  3. Confirm only one top-level <header role="banner"> or <header> exists for the site header.
  4. If your theme outputs a second <header> (e.g. inside a widget area or block), wrap it inside a <section> or <article> element, or change it to a <div>.
  5. If using a block theme (FSE), go to Appearance → Editor → Templates. Select the template, click the wrapping header block, and in the Block Settings sidebar check its HTML element — change duplicate ones to 'div' via the 'Additional CSS class' and block HTML settings.
  6. Install the free 'axe DevTools' Chrome extension and re-scan to confirm the fix.
Landmark no duplicate contentinfoModerate effort

Remove duplicate `<footer>` elements or `role="contentinfo"` landmarks so your page has exactly one, site-wide footer region.

On WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a local IDE/FTP client for safety).
  2. Open your active theme's 'footer.php' file. Check for `<footer` and `role="contentinfo"` tags — there should be only one wrapping element.
  3. Also open 'header.php' and any page-builder template files to check nothing accidentally outputs a second `<footer>` or `role="contentinfo"` outside of footer.php.
  4. If you are using a block theme (Full Site Editing), go to Appearance → Editor → Templates → select your template, then inspect the Footer block. Make sure you have not added a second Footer block to the template.
  5. If a plugin (e.g. footer builder, cookie notice, live chat) injects duplicate footer markup, deactivate plugins one-by-one and re-test with axe to isolate the culprit, then update or replace that plugin.
  6. Use a child theme to make any direct edits to footer.php so updates don't overwrite your fix.
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 WooCommerce

  1. The '<main>' landmark lives in your active WordPress theme. Navigate to Appearance › Theme File Editor (or use a local/staging FTP/SSH client for safety).
  2. Open the theme's 'header.php' and 'page.php' / 'single.php' / 'archive.php' — search each for '<main' and 'role="main"'. Consolidate to exactly one.
  3. If you are using a block theme (Full Site Editing), go to Appearance › Editor › Templates, select the relevant template, switch to Code Editor view, and inspect the markup for duplicate 'core/group' blocks set as 'tagName: main'.
  4. If a page-builder plugin (Elementor, Divi, Beaver Builder) is adding the duplicate, check the plugin's layout settings or its page/post wrapper element and change its HTML tag from 'main' to 'div' or 'section'.
  5. Install the 'axe DevTools' browser extension and re-scan the page to confirm one main landmark.
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 WooCommerce

  1. In WordPress Admin go to Appearance → Theme File Editor (or use a local FTP/SSH client). If your theme is a block theme, use Appearance → Editor instead.
  2. Open `header.php` (classic theme) — find the closing `</header>` tag and add `<main id="main-content" role="main">` immediately after it.
  3. Open `footer.php` — add the corresponding `</main>` tag immediately before the opening `<footer>` tag.
  4. Check `page.php`, `single.php`, `archive.php`, and `woocommerce/` templates to ensure none of them wrap content in a second `<main>` tag (some themes already include one — remove the duplicate).
  5. Add the skip link in `header.php` just after `<body <?php body_class(); ?>>`: `<a class="skip-link screen-reader-text" href="#main-content">Skip to main content</a>`.
  6. Save, then run Lighthouse Accessibility audit in Chrome DevTools to verify the fix.
Landmark uniqueModerate effort

Add a unique aria-label (or aria-labelledby) to every repeated landmark role so assistive technologies can distinguish between them.

On WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a child theme and edit files via FTP/SFTP to avoid overwriting on updates).
  2. Open header.php to find your primary <nav> element — typically output by wp_nav_menu() wrapped in a <nav> tag.
  3. Add aria-label="Primary navigation" to that <nav> tag. For breadcrumbs (often in breadcrumb.php or a plugin template), add aria-label="Breadcrumbs".
  4. For footer navigation, open footer.php and label its <nav> with aria-label="Footer navigation".
  5. If you use a page builder (Elementor, Divi, etc.), switch to its HTML/code block or Custom Attributes panel for each navigation widget and add the aria-label attribute there.
  6. Install the WP Accessibility plugin (by Joe Dolson) which can automate some landmark labelling, then re-run your scanner to verify.
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 WooCommerce

  1. In WordPress admin, go to Appearance → Customize → Additional CSS (for simple overrides) or Appearance → Theme File Editor → style.css for direct theme edits (child theme recommended).
  2. Add or modify a CSS rule targeting inline links, for example: `.entry-content a, .woocommerce-product-details__short-description a { color: #0056b3; text-decoration: underline; }` — replace the color value with one that passes contrast checks.
  3. Alternatively, install a plugin like 'Safe SVG' + 'Custom CSS & JS' or use the built-in Additional CSS panel to scope your fix without touching theme files.
  4. Check WooCommerce product description links specifically by inspecting `.woocommerce-tabs .panel a` and `.product_meta a` selectors.
  5. Save, then verify with the axe browser extension on a product page and a blog post.
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 WooCommerce

  1. In the WordPress admin, go to Appearance → Theme File Editor (or use a child theme via FTP/SSH).
  2. Open the template file containing the problematic link — common locations: header.php or inc/navigation.php for nav icons; woocommerce/loop/loop-start.php or archive-product.php for product card links; footer.php for social icons.
  3. Add aria-label or a visually-hidden <span> to each unnamed link as shown in the code example above.
  4. For the WooCommerce cart icon in the nav, many themes render it via a widget or shortcode — check Appearance → Widgets or Appearance → Customize → Header and look for a 'Cart Icon' option that may expose an alt/label field.
  5. Install the free 'WP Accessibility' plugin (Accessibility → Settings) for automatic fixes to common unlabeled links (e.g. adding skip links and labelling icon-font navigation items).
  6. Test with the axe DevTools browser extension after saving.
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 WooCommerce

  1. Log in to WordPress Admin → Appearance → Theme File Editor (or use a code editor via FTP/SFTP for a safer workflow).
  2. Navigation lists are commonly in 'header.php', 'functions.php' (walker classes), or a dedicated 'nav-menus.php'. Product feature lists are in WooCommerce template overrides under 'yourtheme/woocommerce/'.
  3. Search the relevant template file for '<ul', '<ol', or '<li' and verify that only <li> elements are direct children of any <ul>/<ol>.
  4. To override a WooCommerce core template without losing updates, copy the original file from 'wp-content/plugins/woocommerce/templates/' into 'wp-content/themes/yourtheme/woocommerce/' and then edit the copy.
  5. If a page builder (Elementor, Divi, etc.) built the page, open the page in the builder, locate the List or Nav widget, and fix the markup via the widget settings rather than editing raw HTML.
  6. Save, clear any caching plugins (e.g. WP Rocket, W3 Total Cache), and re-audit with the axe DevTools browser extension.
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 WooCommerce

  1. In your WordPress dashboard, go to Appearance → Theme File Editor (or use a child theme via FTP/SFTP).
  2. Search template files for orphaned `<li>` tags — common culprits are header.php, footer.php, functions.php widget output, or page-builder shortcode output.
  3. If the issue is in a widget (e.g. Custom HTML Widget), go to Appearance → Widgets, open the affected widget, and wrap the `<li>` items in `<ul>…</ul>` directly in the widget's HTML field.
  4. If you use a page builder (Elementor, Divi, WPBakery), open the page, find the element, switch to its HTML/code edit mode, and add the missing `<ul>` wrapper.
  5. If using a block-theme (Full Site Editing), go to Appearance → Editor → find the template part (e.g. Header, Footer) and edit the HTML block.
  6. Save changes and verify with the axe DevTools browser extension.
Meta refreshQuick win

Remove or disable any `<meta http-equiv="refresh">` tag that automatically redirects or reloads the page in under 20 hours.

On WooCommerce

  1. In your WordPress Admin, go to Appearance → Theme File Editor (or use a child theme and edit files via FTP/SFTP).
  2. Open your active theme's 'header.php' file and search for 'http-equiv' or 'refresh'.
  3. Delete the `<meta http-equiv="refresh" ...>` tag if found, then save.
  4. If it is not in 'header.php', check if a plugin is injecting it: go to Plugins → Installed Plugins and deactivate plugins one at a time while retesting, or search plugin files via FTP for the string 'http-equiv=.refresh'.
  5. If you need a URL redirect instead, install a plugin such as 'Redirection' (free) and create a proper 301/302 server-side redirect there.
  6. For live content updates (e.g. stock levels), consult your developer about using WooCommerce's REST API with JavaScript polling rather than a full page refresh.
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 WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a code editor via FTP/SFTP).
  2. Open your active theme's `header.php` (or `functions.php` if the tag is injected via `wp_head`).
  3. Search for `meta name="viewport"` and remove `user-scalable=no` / fix `maximum-scale`.
  4. If you are using a child theme, make the edit in the child theme's `header.php` so it survives updates.
  5. If a page-builder or plugin is injecting the tag, check plugins like Jetpack or your theme's Customizer settings for a 'Mobile' or 'Viewport' option.
  6. Save 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 WooCommerce

  1. In WordPress Admin, go to Appearance → Theme File Editor (or use a file manager / SFTP).
  2. Open your active theme's header.php file.
  3. Search for `<meta name="viewport"` and update the content attribute as described in the generic steps.
  4. If your theme does not show header.php in the editor, use a child theme — create or open your child theme's header.php and override the tag there.
  5. Alternatively, install a plugin such as 'Simple Custom CSS and JS' or 'Code Snippets' and use wp_head hook to output a corrected viewport tag (then ensure the original tag is removed from header.php).
  6. Save and check the rendered source on mobile to confirm only one viewport tag exists.
Nested interactiveModerate effort

Remove or restructure focusable elements nested inside interactive controls so that no interactive element contains another focusable child.

On WooCommerce

  1. In your WordPress Admin go to Appearance → Theme File Editor (or use a child theme via FTP/SFTP).
  2. Product cards are rendered by woocommerce/templates/content-product.php (copy to your-theme/woocommerce/content-product.php to safely override it).
  3. Open that file and find any wrapping <a> or <button> that contains another focusable element inside it.
  4. Refactor using the sibling pattern: use a <div class="product-card"> wrapper, move the product link and Add to Cart button as direct children rather than nested.
  5. If your theme uses a custom product card template, check your-theme/woocommerce/ for overrides and apply the same fix there.
  6. Flush any caching plugin (e.g. WP Super Cache, W3 Total Cache) after saving, then test keyboard navigation on the shop page.
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 WooCommerce

  1. In your WordPress admin, go to the page or product that contains the `<object>` element (Pages, Products, or Posts → Edit).
  2. In the Gutenberg block editor, click the block containing the embed, then click the three-dot menu (⋮) → 'Edit as HTML' to access the raw markup.
  3. Add `aria-label="Descriptive text here"` to the `<object>` opening tag, and/or add fallback text/link between the tags.
  4. If the tag lives in your theme, navigate to Appearance → Theme File Editor and locate the relevant template file (e.g. `single-product.php` or a partial), make the edit there.
  5. For page-builder pages (Elementor, Divi), use the widget's 'Advanced → Custom Attributes' panel to add `aria-label` without touching code.
  6. Update/save and re-test with axe DevTools.
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 WooCommerce

  1. **Using a block theme (FSE):** Go to Appearance → Editor → Templates. Select the template (e.g. Single Product, Product Archive). Click the title block, open Block settings on the right, and change the 'HTML element' or 'Level' setting to H1.
  2. **Using a classic theme:** Go to Appearance → Theme File Editor (or use a child theme). Open the relevant template file (`single-product.php`, `archive-product.php`, `page.php`). Find `the_title()` or `woocommerce_page_title()` and make sure it is inside `<h1>…</h1>`.
  3. **Plugin shortcut:** Install the free 'Headings Map' or 'SEOPress' plugin to audit heading structure across your site without editing code.
  4. Save changes and test with browser DevTools 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 WooCommerce

  1. In your WordPress admin, go to Appearance → Theme File Editor (or use a child theme and a local code editor for safer editing).
  2. Search your active theme's template files and any custom blocks/widgets for 'role="presentation"' and 'role="none"'.
  3. Also check any page builder output (Elementor, Divi, etc.) by inspecting the live page with browser DevTools — identify which widget or block is generating the conflicting element.
  4. If it's in theme files, edit the relevant template file to remove the conflicting ARIA attribute or tabindex as described in the generic steps.
  5. If it's generated by a page builder plugin, edit the widget/element settings and remove any custom ARIA attribute that was added in the plugin's Advanced/Accessibility fields.
  6. Update and clear any caching plugin (e.g. WP Rocket, W3 Total Cache). Re-scan with axe to confirm the fix.
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 WooCommerce

  1. Landmark structure is controlled by your active WordPress theme, not WooCommerce itself. In WordPress Admin go to Appearance → Theme File Editor (or use a code editor via FTP/SSH).
  2. Open `header.php` and confirm the site header uses `<header role="banner">` and navigation uses `<nav aria-label="Primary">` (the `wp_nav_menu()` call should be inside this).
  3. Open `footer.php` and confirm the footer is wrapped in `<footer role="contentinfo">`.
  4. Open `index.php`, `page.php`, `single.php`, and WooCommerce template overrides under `woocommerce/` — ensure each wraps its content in `<main id="main" role="main">`.
  5. If you use a page builder (Elementor, Divi, Beaver Builder), switch to its editor and check that sections are placed inside the theme's `<main>` region; many builders inject content outside it. Use the builder's 'HTML tag' or 'wrapper tag' setting on each section to set semantic tags, or install the 'Accessibility Checker' plugin (Equalize Digital) to pinpoint violations.
  6. Re-run axe DevTools or WAVE on the frontend to confirm all content is inside landmarks.
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 WooCommerce

  1. Access your WordPress dashboard → Appearance → Theme File Editor (or use a child theme / FTP for safer editing).
  2. Search your active theme's template files (e.g., woocommerce/single-product.php, template-parts/, or a page-builder shortcode output) for role="img".
  3. Add aria-label="[descriptive text]" to each flagged element, or aria-hidden="true" for decorative ones.
  4. If the element is generated by a plugin (e.g., a reviews plugin rendering star SVGs), check the plugin's settings for a built-in accessibility label field, or use a Code Snippets plugin to enqueue a small JavaScript fix: document.querySelectorAll('[role="img"]').forEach(el => { if (!el.getAttribute('aria-label') && !el.getAttribute('aria-labelledby')) el.setAttribute('aria-label', 'Rating graphic'); });
  5. Alternatively, install the 'WP Accessibility' plugin which patches several common ARIA label issues automatically.
  6. Test with the axe browser extension after saving.
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 WooCommerce

  1. In your WordPress Admin, go to Appearance → Theme File Editor (or use a child theme) and locate the template that outputs the scrollable region — commonly woocommerce/single-product/description.php or a custom template part.
  2. Find the wrapping element (div, section, etc.) that carries the overflow-scroll CSS class.
  3. Add tabindex="0" and aria-label="Product description" to that element's opening tag.
  4. Add a :focus outline rule to your child theme's style.css: .woocommerce-product-details__short-description:focus { outline: 2px solid #005fcc; }
  5. Alternatively, install the 'WP Accessibility' plugin (by Joe Dolson), which provides helpers for common WCAG fixes, though tabindex may still need to be added manually to custom containers.
  6. Save and verify with keyboard Tab navigation on the front end.
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 WooCommerce

  1. The safest approach is to override the WooCommerce template file rather than editing the plugin directly. Copy the relevant template from 'wp-content/plugins/woocommerce/templates/' into 'wp-content/themes/YOUR-THEME/woocommerce/' maintaining the same folder structure.
  2. Common files to check: 'woocommerce/loop/orderby.php' (sort-by dropdown), 'woocommerce/single-product/add-to-cart/variable.php' (variation selects), 'woocommerce/checkout/form-billing.php' and 'form-shipping.php' (country/state dropdowns).
  3. Open the copied file and locate the <select> element. Add <label for="MATCHING-ID">Descriptive text</label> immediately before it, ensuring the select has a matching id attribute.
  4. If you use a page-builder or custom theme, locate the template part via Appearance → Theme File Editor (or your child theme) and apply the same edit.
  5. Flush caches (WP Super Cache, WP Rocket, etc.), then verify with axe DevTools.
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 WooCommerce

  1. The skip link and its target live in your active WordPress theme. In wp-admin go to Appearance → Theme File Editor (or use a local editor / child theme).
  2. Open header.php. Look for an existing skip link near the top — Storefront theme includes `<a class="skip-link screen-reader-text" href="#content">{{ __('Skip to content') }}</a>`.
  3. Open the file that contains the main content wrapper — usually page.php, single.php, or index.php — and locate the element with `id="content"` (Storefront uses `<div id="content" class="site-content" tabindex="-1">`). Confirm the id value exactly matches the href fragment in the skip link.
  4. If they don't match, update one to match the other. If the id is absent, add it plus `tabindex="-1"` to the outermost content wrapper div or <main> tag.
  5. If you use a page builder (Elementor, Divi), switch to the theme builder's header template and verify the skip link is still the first focusable element output before the <header> section.
Svg img altModerate effort

Add a meaningful text alternative to every SVG image so screen readers can describe it to visually impaired shoppers.

On WooCommerce

  1. In WordPress Admin go to Appearance → Theme File Editor (or use a child theme via FTP/SFTP).
  2. Find the file containing the SVG — often `header.php`, a page-builder block, or an SVG sprite file in the theme's `/assets/` or `/images/` folder.
  3. Edit the `<svg>` tag to add `role='img'` and `aria-labelledby='UNIQUE-ID'`; insert `<title id='UNIQUE-ID'>Descriptive text</title>` as its first child.
  4. If SVGs are embedded via the WordPress Media Library or a page builder (Elementor, Divi), you may need to use a plugin such as 'Safe SVG' which allows editing SVG markup directly, or add custom CSS/JS to inject the attributes.
  5. For inline SVGs in Gutenberg blocks, switch to the Code Editor view of the block and add the title and ARIA attributes manually.
  6. Test using the axe DevTools browser 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 WooCommerce

  1. Log in to WordPress Admin → Appearance → Theme File Editor (or use a child theme and edit files via FTP/SFTP).
  2. Search all theme template files (header.php, footer.php, page.php, woocommerce/ overrides) for 'tabindex' using your code editor's global search.
  3. Remove or correct any positive tabindex values in those files.
  4. Also check any active page-builder plugins (Elementor, Divi, etc.): edit the affected widget/module and look in its 'Advanced' or 'Custom Attributes' section for a tabindex setting — remove or set to 0.
  5. For WooCommerce-specific templates (e.g. checkout form), copy the template to your child theme under woocommerce/ and edit there so updates do not overwrite your changes.
  6. Install the 'Accessibility Checker' plugin (e.g. WP Accessibility or Equalize Digital Accessibility Checker) to re-scan after your changes.
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 WooCommerce

  1. The `lang` attribute on `<html>` is set by WordPress core, not WooCommerce itself — it reads from the site language setting.
  2. Go to WordPress admin → Settings → General → Site Language. Choose the correct language from the dropdown; WordPress will output the proper BCP 47 code automatically.
  3. If the `<html>` tag still shows an invalid value, open your active theme's `header.php` (or the root template file in a block theme: `templates/index.html`) and look for a hard-coded `lang=` attribute overriding the WordPress default. Replace it with `<?php language_attributes(); ?>` which outputs the correct value.
  4. For content in a second language, install the plugin 'Polylang' or 'WPML' and assign the correct language to each post/page — these plugins set `lang` on the `<html>` tag per page automatically.
  5. Save and verify with the axe browser 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 WooCommerce

  1. WooCommerce runs on WordPress hosted on your own server; DNS is managed at your registrar or hosting provider (e.g., cPanel, Cloudflare, SiteGround).
  2. Log in to your DNS provider or cPanel > Zone Editor and find the TXT record named _dmarc.
  3. Edit the record value: change p=none to p=quarantine, save, and monitor DMARC aggregate reports for two to four weeks.
  4. Confirm your WordPress transactional mail plugin (e.g., WP Mail SMTP, FluentSMTP) sending via an SMTP relay (SendGrid, Mailgun, Postmark) has SPF and DKIM properly configured in DNS — check the plugin's settings for DKIM key instructions.
  5. Once reports are clean, edit the _dmarc TXT record again and set 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 WooCommerce

  1. WooCommerce runs on WordPress, so HSTS is set at the server or plugin level, not within WooCommerce itself.
  2. Plugin approach (recommended for non-technical owners): Install the 'Headers Security Advanced & HSTS WP' plugin (or 'WP Force SSL & HTTPS SSL Redirect' which includes HSTS settings) from WordPress Admin → Plugins → Add New. In the plugin settings, enable HSTS and set max-age to 31536000 with 'Include Subdomains' checked.
  3. Server approach (Nginx): Edit your site's Nginx config (usually in /etc/nginx/sites-available/yourdomain.conf) inside the `server { }` block for port 443 and add: `add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;` then run `nginx -t` and `systemctl reload nginx`.
  4. Server approach (Apache): Add `Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"` to your VirtualHost block in your .conf file or .htaccess, then restart Apache.
  5. If hosted on WP Engine, Kinsta, or SiteGround, check their control panel for a 'Security Headers' or 'HSTS' toggle, or contact their support to enable it at the server level.
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 WooCommerce

  1. WooCommerce runs on WordPress with your own hosting, so the HSTS header is set at the web-server or plugin level — WordPress/WooCommerce core does not set it by default.
  2. Option A — Plugin (recommended for non-technical owners): Install the free 'Headers Security Advanced & HSTS WP' plugin (or 'HTTP Headers' by DataCove). Go to Plugins → Add New, search for the plugin name, install and activate it. In the plugin settings, set Strict-Transport-Security max-age to 31536000 and enable includeSubDomains.
  3. Option B — Apache (.htaccess): Open your root .htaccess file via Hosting cPanel → File Manager or via FTP. Inside the '<IfModule mod_headers.c>' block (or add one), insert: Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
  4. Option C — Nginx (nginx.conf or site config): In your server {} block add: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; then reload Nginx: sudo systemctl reload nginx
  5. Verify with DevTools (F12 → Network → any response → Response Headers) 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 WooCommerce

  1. Log in to your hosting control panel (cPanel, Plesk, or your host's dashboard).
  2. Locate the SSL/TLS section and install a free Let's Encrypt certificate for your domain — most hosts (SiteGround, Kinsta, WP Engine, Bluehost) offer a one-click Let's Encrypt install.
  3. Once the certificate is active, in your WordPress Admin go to Settings → General and change both 'WordPress Address (URL)' and 'Site Address (URL)' from http:// to https://.
  4. Install the free 'Really Simple SSL' plugin (Plugins → Add New → search 'Really Simple SSL') and activate it — it handles the 301 redirect, mixed-content fixes, and can enable HSTS.
  5. Alternatively, add redirect rules manually: in your .htaccess file (root of your WordPress install), add: RewriteEngine On / RewriteCond %{HTTPS} off / RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
  6. Flush any caching plugin's cache (e.g. WP Super Cache, W3 Total Cache, WP Rocket) after making changes.
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 WooCommerce

  1. WooCommerce runs on WordPress, which runs on a web server you (or your host) control — the fix lives at the server or hosting level, not inside WordPress itself.
  2. For Apache hosting: connect via SSH or your host's File Manager, open (or create) the `.htaccess` file in your site root, and add: `Header unset Server` and `Header always unset Server`. Ensure `mod_headers` is enabled.
  3. For nginx hosting: ask your host or edit `/etc/nginx/nginx.conf` (or the server block for your site) — add `server_tokens off;` inside the `http {}` or `server {}` block, then run `nginx -s reload`.
  4. For managed WordPress hosts (WP Engine, Kinsta, Flywheel, SiteGround): open a support ticket requesting that the `Server` header be removed or suppressed — many do this automatically or have a one-click security hardening option in their dashboard.
  5. Alternatively, install the 'HTTP Headers' plugin (by WebFactory Ltd) from the WordPress plugin directory: Plugins → Add New → search 'HTTP Headers' → Install & Activate → navigate to Settings → HTTP Headers → Response Headers tab → add a rule to remove the `Server` header.
  6. Verify with `curl -I https://yourstore.com` and check that `Server` is absent or generic.
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 WooCommerce

  1. WooCommerce runs on WordPress + PHP, so the header typically comes from PHP or your host. The fix lives in your server config or a plugin.
  2. Option A — Plugin (no code): Install the free 'HTTP Headers' plugin (by Dimitar Ivanov) or 'Security Headers' plugin. In WordPress Admin → HTTP Headers (or Security Headers), find the X-Powered-By entry and set it to remove/suppress.
  3. Option B — PHP (wp-config.php or functions.php): Add `header_remove('X-Powered-By');` near the top of wp-config.php, or inside a must-use plugin file in /wp-content/mu-plugins/.
  4. Option C — Nginx server block: Add `more_clear_headers 'X-Powered-By';` (requires headers-more module) or handle it via your host's control panel (cPanel → Apache/Nginx configuration).
  5. Option D — Apache .htaccess: Add `Header unset X-Powered-By` (requires mod_headers). Place this in your root .htaccess file above the WordPress rewrite block.
  6. Verify with browser DevTools → Network tab after saving.
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 WooCommerce

  1. Go to WordPress Admin → Plugins → Add New and install the 'Really Simple SSL' plugin (free) or 'Sucuri Security'; these plugins enforce Secure and SameSite cookie flags site-wide via PHP filters.
  2. Alternatively, open your theme's functions.php (Appearance → Theme File Editor → functions.php) or a site-specific plugin and add: `add_filter('session_cookie_params', function($params){ $params['secure'] = true; $params['httponly'] = true; $params['samesite'] = 'Strict'; return $params; });`
  3. For WooCommerce's own session cookie (wp_woocommerce_session_*), it is set by WooCommerce core. Update WooCommerce to the latest version (Dashboard → Updates) to get current security flags.
  4. For WordPress auth cookies (wordpress_logged_in_*, wordpress_sec_*): add `@ini_set('session.cookie_httponly', 1);` and `@ini_set('session.cookie_secure', 1);` to wp-config.php.
  5. Verify via browser DevTools → Application → Cookies that all wp_ and woocommerce_ cookies show HttpOnly ✓, Secure ✓, and SameSite = Strict/Lax.
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 WooCommerce

  1. Install the free WordPress plugin 'Headers Security Advanced & HSTS WP' or 'HTTP Headers' (by RaMMicHaeL) from the WordPress plugin repository: WP Admin → Plugins → Add New → search plugin name → Install & Activate.
  2. Alternatively, add the header directly in your theme's `functions.php` (or a site-specific plugin): `add_action('send_headers', function(){ header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' [your-approved-domains]; object-src 'none'"); });`
  3. Or add it to your `.htaccess` (Apache) file in the public root: `Header set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'"`
  4. For Nginx servers, add `add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'";` inside the relevant `server {}` block in your Nginx config file.
  5. Use the plugin's built-in violation reporting or a free service like report-uri.com to monitor for broken resources before enforcing.
Missing dmarcQuick win

Add a DMARC DNS TXT record at _dmarc.yourdomain.com to protect your domain from email spoofing and phishing.

On WooCommerce

  1. WooCommerce runs on WordPress hosted on your own server — DNS is managed at your domain registrar or hosting provider (e.g. GoDaddy, Namecheap, SiteGround, WP Engine, Cloudflare).
  2. Log into your domain registrar or DNS provider's control panel. Navigate to DNS Management / DNS Zone Editor.
  3. Add a new TXT record: Name/Host = '_dmarc', TTL = 3600 (or default), Value = v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com
  4. WooCommerce stores typically send email via WordPress (wp_mail using PHPMailer) or a plugin like WP Mail SMTP. In your WordPress admin, go to the WP Mail SMTP plugin → Settings and ensure you are sending via an authenticated SMTP provider (e.g. SendGrid, Mailgun, Amazon SES) with SPF and DKIM set up for your domain — this is required for DMARC alignment.
  5. Install a free plugin like 'Check Email' (WP Mail SMTP) to send a test email and verify headers show DKIM and SPF passing before escalating DMARC to p=quarantine or p=reject.
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 WooCommerce

  1. WooCommerce runs on WordPress.org, typically on Apache or Nginx — you can set the header at the server level or via plugin.
  2. Easiest plugin method: Install the free 'Headers Security Advanced & HSTS WP' plugin or 'HTTP Headers' plugin from the WordPress plugin directory (Plugins → Add New → search 'Permissions Policy headers').
  3. In the chosen plugin's settings page, find the Permissions-Policy field and enter your policy value: camera=(), microphone=(), geolocation=(), payment=(), usb=(), fullscreen=(self). Save.
  4. Apache server-level alternative: Add to your .htaccess file (found in the WordPress root directory via FTP/File Manager): `Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"` — requires mod_headers to be enabled.
  5. Nginx server-level alternative: Add to your server {} block in your nginx.conf or site config: `add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;` then reload Nginx.
  6. Verify with Chrome DevTools → Network tab → Response Headers, or use securityheaders.com.
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 WooCommerce

  1. WooCommerce runs on WordPress/Apache or Nginx, so you set this at the server level.
  2. Apache: open your `.htaccess` file (in the WordPress root) and add inside the `<IfModule mod_headers.c>` block: `Header always set Referrer-Policy "strict-origin-when-cross-origin"`
  3. Nginx: open your server block config (e.g. `/etc/nginx/sites-available/yoursite.conf`) and add inside the `server {}` block: `add_header Referrer-Policy "strict-origin-when-cross-origin" always;` — then run `nginx -t` and reload.
  4. Alternatively, install the free WordPress plugin **Headers Security Advanced & HSTS WP** (or **Security Headers** plugin): go to WP Admin → Plugins → Add New, search for the plugin, install and activate, then enable `Referrer-Policy` and select `strict-origin-when-cross-origin` from the dropdown.
  5. Verify with browser DevTools or securityheaders.com.
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 WooCommerce

  1. WooCommerce runs on WordPress hosted at a provider of your choice; DNS is managed at your domain registrar or hosting control panel (cPanel, Cloudflare, etc.) — not inside WordPress itself.
  2. Log in to your hosting control panel (e.g. cPanel → Zone Editor, or your registrar's DNS manager).
  3. Add a TXT record: Name/Host = @ (or your bare domain), TTL = 3600, Value = your SPF string including your web host's mail server and any ESPs you use (e.g. v=spf1 include:_spf.google.com include:sendgrid.net ~all).
  4. WooCommerce transactional emails are sent by your web host's PHP mail or by a plugin like WP Mail SMTP / FluentSMTP — check which SMTP service that plugin is connected to and include that provider's SPF include: directive.
  5. In WordPress admin, go to the plugin settings (e.g. WP Mail SMTP → Settings → Email tab) to confirm which sending service is in use.
  6. Verify with an SPF checker after a few minutes.
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 WooCommerce

  1. WooCommerce runs on WordPress, so HSTS must be set at the server or hosting level — WordPress/PHP itself does not control response headers by default.
  2. Apache: Open your site's .htaccess file (in the public_html/wp root). Inside the `<IfModule mod_headers.c>` block (or add one), insert: `Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"`. Save and reload Apache.
  3. Nginx: Edit your server block config (e.g., /etc/nginx/sites-available/yoursite.conf). Inside the `server { ... }` block for port 443, add: `add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;`. Run `nginx -t` then reload Nginx.
  4. cPanel hosting: Use the 'Headers' module under Apache Configuration, or install the free 'Headers & Options' section in your cPanel's .htaccess editor.
  5. Plugin alternative: Install the free 'HTTP Headers' plugin by WebFactory (WordPress.org). Go to WP Admin → HTTP Headers → Security → toggle on Strict-Transport-Security → set max-age to 31536000, check includeSubDomains → Save.
  6. Verify in browser DevTools → Network → document response headers.
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 WooCommerce

  1. Install the free 'HTTP Headers' plugin (by David Gwyer) or 'Security Headers' plugin from the WordPress plugin directory: WP Admin → Plugins → Add New → search 'HTTP Headers' or 'Security Headers'.
  2. After activating, go to Settings → HTTP Headers (or the plugin's own menu item) and add a new header: Name = X-Content-Type-Options, Value = nosniff. Save.
  3. Alternatively, edit your theme's functions.php (Appearance → Theme File Editor → functions.php) and add: add_action('send_headers', function(){ header('X-Content-Type-Options: nosniff'); });
  4. Or add 'Header always set X-Content-Type-Options "nosniff"' to your .htaccess file (Apache) or 'add_header X-Content-Type-Options nosniff always;' to your nginx server block — both found in your hosting control panel's file manager or via FTP.
  5. Verify by reloading any page and checking Response Headers 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 WooCommerce

  1. Install the free 'Headers Security Advanced & HSTS WP' plugin (or 'HTTP Headers' by John Blackbourn) from WordPress.org plugins — search in WP Admin → Plugins → Add New.
  2. In the plugin settings, enable X-Frame-Options and set the value to SAMEORIGIN (or DENY if you never iframe your own pages).
  3. Alternatively, add the following to your theme's functions.php or a site-specific plugin: add_action('send_headers', function(){ header('X-Frame-Options: SAMEORIGIN'); });
  4. If your hosting uses Nginx, ask your host to add 'add_header X-Frame-Options SAMEORIGIN always;' inside the server{} block; for Apache, add 'Header always set X-Frame-Options SAMEORIGIN' to your .htaccess or VirtualHost config.
  5. Verify by visiting your storefront, opening DevTools → Network → document request → 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 WooCommerce

  1. Create a staging site using your host's staging tool (e.g. WP Engine, Kinsta, SiteGround all offer one-click staging) or duplicate the site manually to a subdomain (e.g. staging.yourstore.com).
  2. Block the staging site from search engines (WordPress Admin → Settings → Reading → 'Discourage search engines') and restrict access by IP or HTTP Basic Auth so DAST traffic stays private.
  3. Download and install OWASP ZAP on your local machine or a test server. Set the target to your staging URL.
  4. Configure an authenticated scan: in ZAP go to Tools → Options → Authentication, supply a WooCommerce test-customer login, and record the session so ZAP can scan the cart, checkout, and My Account pages.
  5. Run 'Active Scan'. Pay special attention to WooCommerce REST API endpoints (/wp-json/wc/v3/) and any custom plugins. Remediate findings before deploying to production.
  6. Consider adding a WordPress security plugin (e.g. Wordfence, Patchstack) to your production site for ongoing monitoring between DAST runs.
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 WooCommerce

  1. WooCommerce itself does not manage SSL — the certificate is handled by your web host or server.
  2. Log in to your hosting control panel (cPanel, Plesk, Kinsta, SiteGround, WP Engine, etc.) and go to the SSL/TLS section.
  3. If using a host with Let's Encrypt integration (most shared hosts): find 'Let's Encrypt' or 'SSL/TLS Status', click 'Renew' or confirm auto-renewal is toggled on for your domain.
  4. If using a paid certificate: download the renewed certificate files from your CA and upload them under SSL/TLS → 'Install and Manage SSL for your site'.
  5. After renewal, install the WordPress plugin 'Really Simple SSL' (free) to verify your entire site runs over HTTPS and to force redirects — it will flag any mixed-content issues introduced by the renewal.
  6. In WordPress Admin → Settings → General, confirm both 'WordPress Address (URL)' and 'Site Address (URL)' start with https://.
Ssl cert invalidModerate effort

Install a valid SSL/TLS certificate that exactly matches your store's domain name, so browsers trust your site and customer data is encrypted in transit.

On WooCommerce

  1. WooCommerce runs on WordPress, so SSL is managed at your web host, not inside WooCommerce itself.
  2. Log in to your hosting control panel (cPanel, Plesk, or host-specific dashboard). Look for 'SSL/TLS', 'Let's Encrypt', or 'SSL Certificates' section.
  3. In cPanel: Security → SSL/TLS → Manage SSL Sites. Check which domain the installed certificate covers. If it is the wrong domain, use 'Let's Encrypt SSL' (AutoSSL) or install a new certificate for the correct domain.
  4. For hosts that offer AutoSSL (e.g. cPanel AutoSSL with Let's Encrypt): Security → SSL/TLS Status → run AutoSSL for your domain.
  5. After installing the correct certificate, in WordPress Admin go to Settings → General and confirm both WordPress Address and Site Address begin with https:// and use the exact domain the certificate covers.
  6. Install the 'Really Simple SSL' plugin (WordPress Admin → Plugins → Add New → search 'Really Simple SSL') to automatically redirect HTTP to HTTPS and fix mixed-content issues site-wide.
  7. Verify with your browser padlock or SSL Labs.
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 WooCommerce

  1. WooCommerce runs on WordPress (self-hosted), so SSL is managed at the hosting layer, not inside WordPress itself.
  2. Log in to your hosting control panel (cPanel, Plesk, Kinsta, WP Engine dashboard, etc.).
  3. Navigate to the SSL/TLS section (cPanel: Security → SSL/TLS or 'Let's Encrypt SSL').
  4. Issue or re-issue a certificate that covers BOTH yourstore.com AND www.yourstore.com as SANs — most hosts offer a free Let's Encrypt certificate covering both with one click.
  5. After installing the new certificate, go to WordPress Admin → Settings → General and ensure both 'WordPress Address' and 'Site Address' start with https://.
  6. Install the 'Really Simple SSL' plugin if you need help forcing HTTPS site-wide and verifying the certificate is being served correctly.
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 WooCommerce

  1. WooCommerce runs on WordPress, which runs on your own hosting — you (or your host) control port 443.
  2. Log in to your hosting control panel (cPanel, Plesk, or your host's dashboard) and navigate to the SSL/TLS section.
  3. Install a free Let's Encrypt certificate: in cPanel go to Security → SSL/TLS → Let's Encrypt™ SSL (or use the 'SSL/TLS Status' tool); click 'Issue' next to your domain.
  4. Ensure your hosting firewall / server security group has port 443 open. In cPanel, check Security → ModSecurity or contact your host to confirm port 443 is not blocked at the network level.
  5. In WordPress Admin → Settings → General, change both 'WordPress Address (URL)' and 'Site Address (URL)' to https://.
  6. Install the 'Really Simple SSL' plugin (free, WordPress.org repository) to handle HTTP→HTTPS redirects automatically and fix mixed-content issues.
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 WooCommerce

  1. SPF is a DNS record controlled at your domain registrar or DNS host, not inside WordPress or WooCommerce.
  2. Log in to your hosting control panel (cPanel, Plesk) or DNS provider. Look for a 'DNS Zone Editor' or 'DNS Management' section.
  3. Find the TXT record for your root domain (@) starting with v=spf1.
  4. If you send transactional email via an SMTP plugin (e.g. WP Mail SMTP with SendGrid, Mailgun, Brevo), ensure that provider's include: statement is present, then add -all at the end.
  5. Example final record: v=spf1 include:sendgrid.net include:_spf.google.com -all
X content type options weakQuick win

Set the X-Content-Type-Options response header to exactly `nosniff` (once, not duplicated) on every page and asset your store serves.

On WooCommerce

  1. Install the free 'HTTP Headers' plugin (author: John Henckel) or 'Headers Security Advanced & CORS' from the WordPress plugin directory.
  2. WordPress Admin → Settings → HTTP Headers (or the plugin's settings page) — find the `X-Content-Type-Options` field and set it to `nosniff`. Save.
  3. If you are also setting the header via your server (see below), disable it in one location to prevent duplication.
  4. Alternatively, edit your `.htaccess` file (Apache) in the WordPress root directory and add: `<IfModule mod_headers.c>` / `Header set X-Content-Type-Options "nosniff"` / `</IfModule>` — but only if you are NOT setting it via a plugin.
  5. For Nginx hosting, ask your host to add `add_header X-Content-Type-Options "nosniff" always;` to the server block in the nginx config, and remove any plugin that also sets it.
  6. Verify: use a browser's DevTools Network tab or securityheaders.com to confirm exactly one `nosniff` value.
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 WooCommerce

  1. Install the free plugin 'HTTP Headers' (by David Gwyer) or 'Security Headers' from the WordPress plugin directory.
  2. In WordPress admin go to Settings → HTTP Headers (or the plugin's menu), find X-Frame-Options, set the value to DENY, and save.
  3. Alternatively, edit your theme's functions.php (or a site-specific plugin): add add_action('send_headers', function(){ header('X-Frame-Options: DENY'); });
  4. Or add it directly in your .htaccess file (Apache): Header always set X-Frame-Options "DENY"
  5. Or in Nginx server block: add_header X-Frame-Options "DENY" always; — place inside the server {} block and reload Nginx.
  6. Verify with DevTools → Network → document response headers.

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 WooCommerce

  1. Log in to WordPress Admin → Dashboard → Updates: you will see available updates for WordPress core, WooCommerce plugin, themes, and all other plugins.
  2. Click 'Update Now' for WordPress core first, then WooCommerce, then all remaining plugins and your active theme.
  3. Navigate to WooCommerce → Status → System Status to see a full report of your WooCommerce version, WordPress version, PHP version, and any environment issues flagged in red.
  4. Enable automatic background updates for minor releases: in WordPress Admin → Settings, or by adding `define('WP_AUTO_UPDATE_CORE', true);` to your wp-config.php.
  5. Schedule a monthly review and use a staging environment to test major updates before applying them to your live store.
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 WooCommerce

  1. WooCommerce does not manage domains. Your domain is registered wherever you originally purchased it (e.g. GoDaddy, Namecheap, your web host's registrar).
  2. Log in to that registrar's control panel, find your domain, and enable auto-renew.
  3. If your host (e.g. SiteGround, Bluehost, WP Engine) also registered your domain, log in to the hosting dashboard → Domains section and enable auto-renew there.
  4. Confirm that the billing email on the registrar account matches an inbox you actively monitor.
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 WooCommerce

  1. Install the free WordPress plugin 'GTM4WP' (duracell0, WordPress.org plugin directory): Plugins → Add New → search 'GTM4WP' → Install → Activate.
  2. Go to Settings → Google Tag Manager, enter your GTM Container ID (GTM-XXXXXX), and enable the plugin.
  3. Inside GTM4WP settings, enable 'Track eCommerce' under the Integration tab — this automatically pushes view_item, add_to_cart, begin_checkout, and purchase events to the dataLayer.
  4. In GTM, create a GA4 Configuration tag with your Measurement ID firing on All Pages, then create GA4 Event tags listening to the custom events pushed by GTM4WP.
  5. Use GTM Preview + GA4 DebugView to verify, then publish the container.
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 WooCommerce

  1. In your WordPress admin, go to Appearance › Theme File Editor (or use an FTP/file manager).
  2. Select your active theme in the top-right dropdown, then open header.php (or the equivalent root template — some block themes use html.php or parts/header.html).
  3. Locate the <html opening tag. WordPress typically outputs it via the language_attributes() template tag, which automatically inserts the correct lang attribute based on your site's configured language.
  4. If language_attributes() is already present (e.g., <html <?php language_attributes(); ?>>), the lang value is controlled by Settings › General › Site Language in the WordPress admin — set that to your correct language there.
  5. If the tag is hard-coded without language_attributes(), replace it with: <html <?php language_attributes(); ?>>
  6. Save, then check View Page Source on the frontend to confirm lang= appears in the <html> tag.
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 WooCommerce

  1. WooCommerce generates Product schema through either its built-in output or via an SEO plugin. First, check which plugin controls your schema: go to WP Admin > Plugins > Installed Plugins and look for Yoast SEO, Rank Math, or Schema Pro.
  2. If using Yoast SEO (v14+): Yoast reads WooCommerce's stock status automatically and should output the correct availability. Confirm the product's stock status is actually set to 'Out of stock' in the product edit screen under Inventory > Stock status.
  3. If using Rank Math: go to WP Admin > Rank Math > Status & Tools > Schema, confirm 'WooCommerce' integration is enabled. Rank Math also reads stock status dynamically — ensure the product's stock status in WooCommerce is correctly set.
  4. If schema is output by your theme or a standalone schema plugin (e.g. Schema Pro, WP Schema Pro): open the plugin settings and verify it maps 'availability' to the WooCommerce stock status field, not a hard-coded value.
  5. To bulk-update stock status for multiple products: WP Admin > Products > All Products > filter by stock status, select products, use Bulk Actions > Edit > change Stock status to 'Out of stock'.
  6. Validate a sold-out product URL 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 WooCommerce

  1. In WordPress admin, go to Products → All Products. Add the 'Categories' column (Screen Options → enable Categories) and sort/filter to find products with no category assigned.
  2. Click Edit on each orphaned product, scroll to the 'Product categories' metabox in the right sidebar, and assign it to at least one relevant category.
  3. Ensure categories appear in your navigation: go to Appearance → Menus, add the relevant product categories to your primary menu.
  4. To add related/upsell links: on the product edit screen, scroll to Product data → Linked Products tab, and add 'Upsells' or 'Cross-sells' pointing to and from the orphaned product.
  5. Install a plugin like 'YITH WooCommerce Related Products' or 'WooCommerce Product Recommendations' to automate related-product sections across all product pages.
  6. For blog-based contextual links: edit relevant Posts or Pages using the WordPress block editor and hyperlink product names to their URLs.
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 WooCommerce

  1. If you are using Yoast SEO (most common): go to WordPress Admin → SEO → General → Features → make sure XML Sitemaps is toggled ON.
  2. Then go to SEO → Search Appearance → Content Types → Products → ensure 'Show Products in search results' is set to YES (if it's NO, products are excluded from the sitemap).
  3. View your product sitemap at yourdomain.com/product-sitemap.xml and verify all live products are listed.
  4. Alternative: if using Rank Math, go to WordPress Admin → Rank Math → Sitemap Settings → Products → make sure the Products post type is enabled.
  5. Resubmit the sitemap URL 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 WooCommerce

  1. WordPress Admin → Products → Categories: review your category tree. Avoid nesting categories more than two levels deep (parent → child is fine; parent → child → grandchild pushes products to depth 4+).
  2. Reassign deeply nested products to a shallower category: edit the product, scroll to 'Product categories' in the right panel, tick the shallower parent category as well as (or instead of) the deep subcategory.
  3. Appearance → Menus: add direct links to buried product categories or individual products in your primary navigation menu, keeping the menu hierarchy flat (max one sub-level).
  4. Use a page builder (Elementor, Kadence Blocks) or a WooCommerce shortcode ([products ids='...' ] or [product_category category='...']) on the homepage or a top-level category page to display buried products directly.
  5. Install a mega-menu plugin (e.g. Max Mega Menu, WP Mega Menu) to surface many product categories simultaneously without adding click depth.
  6. Verify with Screaming Frog or Sitebulb: crawl the site and sort by 'Crawl Depth' to confirm no product page exceeds 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 WooCommerce

  1. In your WordPress admin, install the free plugin 'Really Simple SSL' (Plugins → Add New → search 'Really Simple SSL') — it performs a sitewide HTTP→HTTPS redirect and fixes many mixed-content issues automatically by filtering content URLs.
  2. Go to Settings → SSL (added by Really Simple SSL) and enable 'Mixed content fixer' to replace HTTP URLs in dynamically rendered content.
  3. For database-stored content (product descriptions, page builder content, widget text), run a search-replace in the database: use the free plugin 'Better Search Replace' (Plugins → Add New) to replace 'http://yourdomain.com' with 'https://yourdomain.com' across all tables — always take a full database backup first.
  4. Check your active theme and child theme files (Appearance → Theme File Editor) and any custom plugins for hardcoded HTTP asset URLs; update to HTTPS.
  5. For WordPress.org sites with server access, add the following to your wp-config.php as a safety net: define('FORCE_SSL_ADMIN', true); and ensure your .htaccess has a full HTTP→HTTPS redirect rule.
  6. Add <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> to your theme's header.php (inside <head>) or via a plugin like 'Header and Footer Scripts'.
  7. Reload your site in an incognito browser window and check the padlock/console for any remaining warnings.
Mobile viewportQuick win

Confirm your store has a correct responsive viewport meta tag so it displays properly on phones and tablets.

On WooCommerce

  1. In your WordPress Admin, go to Appearance → Theme File Editor (or use a child theme via FTP/SFTP).
  2. Select your active (child) theme in the right-hand dropdown and open 'header.php'.
  3. Search for 'viewport'. Most modern WordPress/WooCommerce themes include it automatically.
  4. If missing, add <meta name="viewport" content="width=device-width, initial-scale=1"> inside the <head> tag, ideally before any stylesheet links.
  5. Alternatively, if your theme uses a functions.php hook system, you can add it via wp_head() action: add_action('wp_head', function(){ echo '<meta name="viewport" content="width=device-width, initial-scale=1">'; }, 1);
  6. Save and 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 WooCommerce

  1. WooCommerce runs on WordPress, which runs on a server you (or your host) control — the fix is applied at the server or hosting level, not inside WordPress itself.
  2. For Apache hosting (most shared hosts): access your hosting control panel (cPanel → File Manager, or SSH), open or create a `.htaccess` file in your site root, and add: `ServerSignature Off` and `Header unset X-Powered-By`. Also ask your host to set `ServerTokens Prod` in the main Apache config (httpd.conf) if you don't have root access.
  3. For Nginx hosting: edit your server block config (typically in /etc/nginx/sites-available/yoursite or /etc/nginx/nginx.conf) and add `server_tokens off;` inside the `http {}` or `server {}` block. Add `more_clear_headers 'Server'; more_clear_headers 'X-Powered-By';` if the headers_more module is available.
  4. For managed WordPress hosts (WP Engine, Kinsta, Flywheel, SiteGround, etc.): open a support ticket — these hosts typically handle header suppression on request or by policy, as you don't have access to the server config directly.
  5. Alternatively, install the free plugin 'Security Headers' or 'HTTP Headers' from the WordPress plugin repository: Dashboard → Plugins → Add New → search 'HTTP Headers' → Install & Activate → configure to remove Server and X-Powered-By headers.
  6. Verify with browser DevTools (F12 → Network → reload page → click document request → Response Headers).
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 WooCommerce

  1. WooCommerce itself does not manage SSL — your certificate is controlled at the hosting or server level.
  2. Log into your hosting control panel (cPanel, Plesk, Kinsta, WP Engine, SiteGround, etc.) and navigate to SSL/TLS or Security → SSL.
  3. If your host uses Let's Encrypt (most do), find the 'Auto-Renew' or 'Auto SSL' toggle and confirm it is enabled.
  4. For paid certificates managed outside your host, note the expiry date shown in the SSL dashboard and set a renewal reminder 60 days out.
  5. After renewal, visit WooCommerce → Settings → General and confirm both 'WordPress Address' and 'Site Address' begin with https://.
  6. Install a plugin such as 'Really Simple SSL' to catch any mixed-content warnings after a certificate change.

Generative Engine Optimization · 9 fixes

Ai crawler blockedQuick win

Your robots.txt explicitly blocks one or more AI assistants from crawling your store, so those engines can't discover, index, or cite your products when shoppers ask for recommendations.

On WooCommerce

  1. If a physical robots.txt exists at your site root, edit it via SFTP/your host file manager.
  2. Otherwise edit it in Yoast SEO (Tools > File editor) or Rank Math (General Settings > Edit robots.txt).
  3. Remove the 'Disallow: /' line under the AI bot's 'User-agent' block, or add an explicit 'User-agent: <bot>' followed by 'Allow: /'.
  4. Save and load https://yoursite.com/robots.txt to verify.
Ai crawler not specifiedQuick win

Your robots.txt doesn't explicitly allow several AI crawlers. They aren't blocked, but a number of AI engines act on explicit permission, so naming them removes any ambiguity.

On WooCommerce

  1. If a physical robots.txt exists at your site root, edit it via SFTP/your host file manager.
  2. Otherwise edit it in Yoast SEO (Tools > File editor) or Rank Math (General Settings > Edit robots.txt).
  3. Remove the 'Disallow: /' line under the AI bot's 'User-agent' block, or add an explicit 'User-agent: <bot>' followed by 'Allow: /'.
  4. Save and load https://yoursite.com/robots.txt to verify.
Geo brand name inconsistentModerate effort

Your brand name appears in multiple different formats across your site (schema, og:site_name, footer, logo). AI engines may treat the variants as separate entities.

On WooCommerce

  1. WooCommerce + Yoast/Rank Math output Product and Organization schema automatically; make sure the SEO plugin's schema/social settings are filled in (org name, logo, social profiles for sameAs).
  2. For missing Product fields, ensure the product has price, image, SKU, and brand set in the product data tabs.
  3. Validate with Google's Rich Results Test.
Geo eeat trust signals weakModerate effort

Your store is missing trust signals (E-E-A-T) that AI engines and shoppers look for before recommending a store: clear policies, real contact details, and a credible business identity.

On WooCommerce

  1. WooCommerce + Yoast/Rank Math output Product and Organization schema automatically; make sure the SEO plugin's schema/social settings are filled in (org name, logo, social profiles for sameAs).
  2. For missing Product fields, ensure the product has price, image, SKU, and brand set in the product data tabs.
  3. Validate with Google's Rich Results Test.
Geo faqpage missing productsModerate effort

Your product pages don't use FAQPage schema, so AI assistants can't pull direct answers to common buyer questions (sizing, materials, shipping) about your products.

On WooCommerce

  1. WooCommerce + Yoast/Rank Math output Product and Organization schema automatically; make sure the SEO plugin's schema/social settings are filled in (org name, logo, social profiles for sameAs).
  2. For missing Product fields, ensure the product has price, image, SKU, and brand set in the product data tabs.
  3. Validate with Google's Rich Results Test.
Geo price not crawlableModerate effort

Your product prices appear to be rendered by JavaScript and may not be present in the static HTML, so AI crawlers (which often don't run JS) can't read or cite them.

On WooCommerce

  1. WooCommerce + Yoast/Rank Math output Product and Organization schema automatically; make sure the SEO plugin's schema/social settings are filled in (org name, logo, social profiles for sameAs).
  2. For missing Product fields, ensure the product has price, image, SKU, and brand set in the product data tabs.
  3. Validate with Google's Rich Results Test.
Geo schema org missing sameasModerate effort

Your Organization schema has no sameAs links to your social profiles, Wikipedia, or other authoritative sources - so AI engines can't confidently connect the dots about your brand.

On WooCommerce

  1. WooCommerce + Yoast/Rank Math output Product and Organization schema automatically; make sure the SEO plugin's schema/social settings are filled in (org name, logo, social profiles for sameAs).
  2. For missing Product fields, ensure the product has price, image, SKU, and brand set in the product data tabs.
  3. Validate with Google's Rich Results Test.
Geo schema product incompleteModerate effort

Your Product structured data is missing fields AI engines rely on to cite your products accurately (price, availability, currency, brand, ratings).

On WooCommerce

  1. WooCommerce + Yoast/Rank Math output Product and Organization schema automatically; make sure the SEO plugin's schema/social settings are filled in (org name, logo, social profiles for sameAs).
  2. For missing Product fields, ensure the product has price, image, SKU, and brand set in the product data tabs.
  3. Validate with Google's Rich Results Test.
Llms txt missingQuick win

Your store has no llms.txt - the emerging standard (like robots.txt, but for AI) that tells language models which content to read and cite. SEOLZ has generated a ready-to-upload file from your own pages.

On WooCommerce

  1. Create a plain text file named llms.txt and paste the generated content.
  2. Upload it to your WordPress site root (same folder as robots.txt / wp-config.php) via SFTP or your host file manager.
  3. Verify at https://yoursite.com/llms.txt.