Every WordPress.org fix we catalog
SEOLZ catalogs 45 fixes for WordPress.org across 5 areas — SEO, answer-engine readiness, accessibility, security and site-health. Each lists the exact steps for WordPress.org, with a link to the official docs.
SEO · 13 fixes
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 WordPress.org
- Install WP Rocket or Perfmatters (Plugins → Add New → search) for an all-in-one performance plugin that handles preloading, CSS/JS deferral, and lazy-load exclusions.
- Install Imagify or ShortPixel to auto-convert existing and newly uploaded images to WebP with automatic <picture> tag fallback — no manual image re-export needed.
- In WP Rocket → Media: find 'Exclude from LazyLoad' and add the CSS selector or filename of your hero image (e.g., .hero-image, hero-banner.jpg) so it loads eagerly.
- In WP Rocket → Preload: enable 'Preload Links' and use the 'Prefetch DNS Requests' field to add your CDN domain.
- Manually add a preload tag for maximum control: Appearance → Theme File Editor → header.php → inside <head> add: <link rel='preload' as='image' href='/wp-content/uploads/hero.webp' fetchpriority='high'>.
- Verify with PageSpeed Insights mobile score and WP Rocket's Cache Lifespan settings to ensure the optimized page is served from cache to all visitors.
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 WordPress.org
- Install Yoast SEO or Rank Math if not already active (Plugins → Add New → search → Install → Activate).
- For any post or page: open the editor → scroll to the Yoast SEO / Rank Math panel → click 'Edit snippet' (Yoast) or expand 'General' (Rank Math) → type your unique description in the Meta Description field → Update/Publish.
- For bulk editing: Yoast SEO → Tools → Bulk editor → Descriptions tab — edit descriptions for all content in a single table.
- For archive/author pages: Yoast → Settings → Content types → Authors/Archives → Meta description template.
- For paginated archives (/page/2 etc.): Yoast and Rank Math automatically add `rel=canonical` to page 1 by default, preventing duplicate description penalties for pagination.
Duplicate titleModerate effort
Write a unique, descriptive title tag for every page on your store so no two pages share the same title.
On WordPress.org
- Install Yoast SEO or Rank Math if not already active.
- For individual posts/pages: open the editor, scroll to the Yoast SEO / Rank Math panel, click 'Edit snippet', and type a unique SEO title.
- For sitewide templates: Yoast SEO → Search Appearance → Content Types / Taxonomies; set patterns with %%title%% %%sep%% %%sitename%% so every page outputs a distinct title.
- Use Yoast's Bulk Editor (Yoast SEO → Tools → Bulk Editor → Titles) to review and edit all page titles in one place without opening each post.
- If using a theme that outputs its own <title> tag and conflicts with the SEO plugin, ensure the theme declares add_theme_support('title-tag') in functions.php and removes any hardcoded <title> in header.php.
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 WordPress.org
- Install Yoast SEO or Rank Math (Plugins → Add New → search for the plugin → Install → Activate).
- Both plugins output self-referencing canonical tags automatically for every post, page, and custom post type.
- To override the canonical on a specific page: open the post/page editor → find the Yoast SEO or Rank Math meta box → click 'Advanced' → enter the preferred URL in the 'Canonical URL' field.
- If you are using a custom theme without an SEO plugin, add `<link rel="canonical" href="<?php echo esc_url( get_permalink() ); ?>">` inside the `<head>` section of your `header.php` file.
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 WordPress.org
- **Pages & Posts (Gutenberg):** Edit the page or post → the large 'Add title' field at the very top of the editor IS the H1. Make sure every page has a descriptive title entered here.
- **Theme check:** Some themes suppress or re-tag the post title. Go to Appearance → Theme File Editor → open 'single.php', 'page.php', or 'archive.php' and check that `the_title()` or `get_the_title()` is wrapped in `<h1>`. If using a child theme, override these files there.
- **Page builders (Elementor, Divi, etc.):** The theme title may be hidden. Open the page in the builder → look for a 'Post Title' or 'Page Title' widget → ensure it is present and set to H1. Alternatively, add a Heading widget and set it to H1.
- **WooCommerce product pages:** The product name field in WooCommerce renders as H1 by default — see WooCommerce platform steps above.
- **SEO plugins:** Yoast SEO (Admin → SEO → Search Appearance) and Rank Math (Admin → Rank Math → Titles & Meta) both flag missing H1s in their page analysis — use their 'Content Analysis' panel on each edit screen to confirm the H1 is detected.
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 WordPress.org
- Install Yoast SEO or Rank Math (Admin → Plugins → Add New).
- For any post or page: open the editor → find the Yoast SEO / Rank Math block at the bottom → click 'Edit snippet' → type in the Meta description field → Update/Publish.
- For the homepage (static page): edit that page the same way; if using 'Latest Posts' as homepage, go to SEO (Yoast) → Search Appearance → Content Types → Posts → set a homepage description template.
Missing titleQuick win
Add a unique, descriptive title tag (30–60 characters) to every page that is missing one.
On WordPress.org
- Install Yoast SEO (free) or Rank Math (free) from Plugins → Add New.
- Edit any post or page → scroll to the Yoast SEO or Rank Math meta box → click into the 'SEO title' field and type your title.
- For site-wide title templates: Yoast SEO → Settings → Content types; Rank Math → Titles & Meta → select content type and set the title format.
- Click Update/Publish.
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 WordPress.org
- In WordPress admin, navigate to Appearance → Theme File Editor. Always work in a child theme to avoid losing changes on updates.
- Open the template for the affected page type (single.php, page.php, index.php, archive.php) and search for '<h1'.
- Ensure only the primary content title — <?php the_title('<h1>', '</h1>'); ?> — uses the H1 tag. Convert all others to <h2> or <h3>.
- If using Gutenberg (block editor): open the page/post, click any Heading block that is set to H1 but is not the main title, and use the block toolbar to change it to H2 or H3.
- Install a heading-audit plugin or use the 'Headings Map' browser extension to scan all flagged pages after saving.
- Use an SEO plugin (Rank Math or Yoast SEO) to get a per-post H1 check under the 'SEO Analysis' tab in the post editor.
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 WordPress.org
- Check Settings → Reading → confirm 'Discourage search engines from indexing this site' is NOT ticked — this is the single most common accidental site-wide noindex on WordPress.
- For Yoast SEO: edit the page/post → Yoast SEO meta box → Advanced tab → 'Allow search engines to show this in results' → set to 'Yes'.
- For Rank Math: edit the page/post → Rank Math sidebar → Advanced → Robots Meta → ensure 'No Index' is unchecked.
- For All in One SEO: edit the page → AIOSEO Settings box → Advanced → Robots Settings → set 'No Index' to off.
- For theme-level noindex: Appearance → Theme File Editor → search `noindex` across template files and remove any `<meta name="robots">` tag containing it.
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 WordPress.org
- Most non-self canonical issues in WordPress are caused by SEO plugins or theme code.
- If using Yoast SEO: open the post/page/product editor → scroll to the Yoast SEO meta box → click 'Advanced' → check the 'Canonical URL' field and clear any value that is not the page's own URL.
- If using Rank Math: open the editor → Rank Math sidebar or meta box → 'Advanced' tab → 'Canonical URL' — clear incorrect values.
- For a site-wide issue, check your theme's header.php (or equivalent) for a hard-coded <link rel="canonical"> tag and remove it, letting the SEO plugin handle canonicals.
- Also check for conflicting plugins: deactivate plugins one-by-one or search the active plugins list for others that output canonical tags (e.g., All in One SEO, The SEO Framework).
- Verify the fix by viewing page source and searching for 'canonical'.
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 WordPress.org
- Install Yoast SEO or Rank Math if not already active.
- In Yoast SEO: SEO → Search Appearance → Archives → Search Pages → toggle noindex ON.
- In Rank Math: Rank Math → Titles & Meta → Search Results → enable 'No Index'.
- Edit your robots.txt (accessible via Yoast SEO → Tools → File Editor or directly on the server at the WordPress root) and add: Disallow: /?s=
- Confirm by visiting /?s=anyterm and checking View Source for <meta name="robots" content="noindex">
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 WordPress.org
- Install a caching + performance plugin (e.g. WP Rocket, LiteSpeed Cache, or W3 Total Cache) via Dashboard → Plugins → Add New. Enable page caching, browser caching, and GZIP compression in the plugin settings.
- Minify CSS/JS: Within your performance plugin's settings, enable CSS and JS minification and combination. Use the 'safe mode' or exclusion lists to prevent breaking critical WooCommerce or checkout scripts.
- Images: Install an image optimization plugin (Imagify, ShortPixel, or Smush) and run a bulk optimization on existing Media Library images. Enable WebP conversion and lazy loading.
- Fonts: In Appearance → Theme Editor (or your theme's customizer), reduce the number of Google Fonts loaded. Many performance plugins offer a 'host Google Fonts locally' option to avoid external DNS lookups.
- Plugins audit: Go to Plugins → Installed Plugins and deactivate and delete any plugins not actively in use. Use Query Monitor (free plugin) to identify plugins causing slow database queries.
- Hosting/server: If TTFB is above 600ms, contact your host about enabling Redis/Memcached object caching, or migrate to a managed WordPress host. Check PHP version in Tools → Site Health and upgrade to PHP 8.x if below.
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 WordPress.org
- Install the Yoast SEO or Rank Math plugin if not already active.
- Edit the page or post in the WordPress block editor (Gutenberg) or classic editor.
- Scroll down to the Yoast SEO / Rank Math panel below the content area.
- Click on the 'SEO title' field and update it with your new 50–60 character title. The snippet preview will update in real time and a colour-coded meter shows title length.
- Click 'Update' or 'Publish' to save.
Answer Engine Optimization · 9 fixes
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 WordPress.org
- Edit the target page or post in WordPress Admin → Pages/Posts → Edit.
- In Gutenberg, add a Paragraph block at the very start of content with your concise direct answer.
- Use Heading blocks set to H2 or H3 with question-format text for each section.
- Install 'Rank Math SEO' (free) — it adds a dedicated FAQ Block to Gutenberg. Insert it, fill in Q&A pairs, and Rank Math automatically outputs valid FAQPage JSON-LD.
- Alternatively, use 'Yoast SEO Premium' which includes an FAQ block with the same automated schema output.
- Update/publish the page and test with Google's Rich Results Test.
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 WordPress.org
- Install Yoast SEO or Rank Math (free versions) from Plugins › Add New — both automatically generate Article schema for posts and pages.
- In Yoast SEO: enable breadcrumbs at Yoast SEO › Search Appearance › Breadcrumbs › Enable breadcrumbs, then replace your theme's breadcrumb output with the Yoast breadcrumb function: <?php if (function_exists('yoast_breadcrumb')) { yoast_breadcrumb('<nav>','</nav>'); } ?>
- Per-post schema type is set in the Yoast SEO metabox at the bottom of the post editor › Schema tab › choose Article / BlogPosting / NewsArticle.
- For manual control without a plugin, add a wp_head action hook in functions.php that outputs JSON-LD using get_the_title(), get_the_author(), get_the_date('c'), and get_permalink().
- Validate any post URL using 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 WordPress.org
- Install 'Rank Math SEO' (free) from Plugins › Add New.
- Edit the page with FAQ content using the block editor. In the block inserter, search for 'Rank Math FAQ' and add the FAQ block — fill in your questions and answers. Rank Math automatically wraps this in valid FAQPage JSON-LD.
- For BreadcrumbList: in Rank Math › General Settings › Breadcrumbs, enable breadcrumbs and configure the separator/home label. Add <?php rank_math_the_breadcrumbs(); ?> to your theme template where you want the trail displayed — the JSON-LD is emitted automatically.
- If you prefer Yoast SEO: use the Yoast FAQ Gutenberg block (Yoast SEO Premium) for FAQPage schema, and enable breadcrumbs under SEO › Search Appearance › Breadcrumbs.
- For manual insertion without a plugin: add the JSON-LD via a Code Snippets plugin (search 'Code Snippets' in Add New Plugins) using a PHP snippet with wp_head hook.
- 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 WordPress.org
- Install Rank Math SEO or Yoast SEO (free tiers support HowTo schema).
- For Rank Math: edit the page, open Rank Math sidebar › Schema › Add Schema › HowTo; fill in each step via the UI — no code required.
- For manual control: in your child theme's functions.php add an action on wp_head to echo a <script type='application/ld+json'> block conditionally using is_page() or a custom page template check.
- Rank Math and Yoast both auto-output BreadcrumbList when breadcrumbs are enabled in their settings (Settings › Titles & Metas › Breadcrumbs for Yoast; Rank Math › General Settings › Breadcrumbs).
- 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 WordPress.org
- Install 'Rank Math SEO' (free) from Plugins → Add New and activate it.
- Go to Rank Math → Titles & Meta → Local SEO, toggle Local SEO on, choose your business type, and fill in name, address, phone, and hours.
- Rank Math injects the compliant JSON-LD automatically on every page — no code editing needed.
- If you prefer manual injection without a plugin, go to Appearance → Theme File Editor → open your child theme's header.php and paste the script block before </head>.
- Validate at Google's Rich Results Test after saving.
Missing schema organizationQuick win
Add Organization schema markup to your store's homepage so search engines and AI systems can definitively identify your business, logo, and social profiles.
On WordPress.org
- Install 'Rank Math SEO' or 'Yoast SEO' from Plugins → Add New.
- For Rank Math: navigate to Rank Math → Titles & Meta → Local SEO, enable it, and fill in organization details and social profiles.
- For Yoast SEO: navigate to Yoast SEO → Settings → Site representation, select 'Organization', and complete name, logo, and social fields.
- To add manually: in Appearance → Theme File Editor, open your child theme's header.php and paste the <script type='application/ld+json'>…</script> block before </head>, OR add it via a wp_head action hook in functions.php.
- Verify with Google's Rich Results Test.
Missing schema webpageModerate effort
Add WebPage (and Organization) JSON-LD schema markup to every page so search engines and AI answer engines can confidently understand and describe your site's content.
On WordPress.org
- Install Yoast SEO or RankMath SEO — both output WebPage and Organization schema automatically.
- For Yoast: Yoast SEO → Settings → Site representation → choose 'Organization', fill in name, logo URL, and social profile URLs. WebPage schema is output automatically on every page.
- For RankMath: RankMath → Titles & Meta → set a default Schema Type of 'WebPage'. Configure Organization details under RankMath → General Settings → Local SEO.
- To add schema manually without a plugin, add a wp_head action hook in your child theme's functions.php that outputs the JSON-LD <script> block, using get_the_title() and get_permalink() for dynamic values.
- Validate using Google's Rich Results Test.
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 WordPress.org
- If using Yoast SEO: go to Yoast SEO → Settings → Site representation → fill in organization details and 'Other profiles' URLs; they are output automatically as `sameAs`.
- If using Rank Math: go to Rank Math → Titles & Meta → Local SEO → Social Profiles and enter each profile URL.
- For manual implementation: add the JSON-LD to your child theme's `functions.php` using `add_action('wp_head', function(){ echo '<script type="application/ld+json">...</script>'; });`, or use the 'Insert Headers and Footers' plugin.
- Verify 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 WordPress.org
- In WordPress admin, go to the relevant post/page and confirm its status is 'Published' and visibility is 'Public'.
- Check Settings → Reading — ensure 'Discourage search engines from indexing this site' is unchecked.
- Use your SEO plugin (Yoast SEO, Rank Math, or All in One SEO): open the page editor → SEO meta box → Advanced tab → confirm 'Allow search engines to index this page' (no noindex override is set).
- For broken URLs, install the Redirection plugin (Tools → Redirection → Add Redirect) to 301-redirect old broken paths to the correct live page.
- To add structured data, use your SEO plugin's built-in schema features (Yoast SEO Premium or Rank Math's Schema module supports Product, FAQ, Breadcrumb schema) or install 'Schema Pro' for granular control. Add JSON-LD manually by inserting a Custom HTML block in the block editor if needed.
- After publishing, submit the URL via Google Search Console URL Inspection → Request Indexing, then validate with the Rich Results Test.
Accessibility (WCAG) · 7 fixes
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 WordPress.org
- Go to Appearance → Theme File Editor, or edit via FTP/SFTP using a child theme.
- Search all PHP and HTML template files for the disallowed `role` attribute string.
- Edit the offending file: remove the invalid role from interactive elements, or correct it on non-interactive ones.
- If the markup is generated by a block (Gutenberg) or plugin shortcode, check the block's HTML settings in the Block Editor sidebar or the plugin's output template.
- Save changes and confirm the fix with a browser accessibility audit.
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 WordPress.org
- Go to WP Admin → Settings → General and set 'Site Language' to your correct language.
- In your active (child) theme's `header.php`, ensure the `<html>` tag uses the WordPress template tag: `<html <?php language_attributes(); ?>>`. This outputs the correct `lang` attribute automatically.
- If you don't have a child theme, create one before editing to avoid losing changes on theme updates.
- Save and verify via page source.
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 WordPress.org
- For theme-controlled forms (search, comments, newsletter), open your child theme's template files in Appearance → Theme File Editor or via FTP. Locate `searchform.php`, `comments.php`, or the relevant template part and add a `<label for="s">Search</label>` (or equivalent) before each unlabeled input.
- For plugin-generated forms (Contact Form 7, Gravity Forms, WPForms, etc.): open the form builder in the plugin's admin area, select the field, and enable 'Show label' or fill in the 'Field Label' setting. Never leave the label blank and rely only on placeholder text or a tooltip.
- If a label must be visually hidden for design reasons, use a CSS class like `.screen-reader-text` (built into most WordPress themes) on the `<label>` element rather than removing the label or using `title` alone — this keeps the label accessible to screen readers while hiding it visually.
- Save and verify with the axe browser extension or WP Accessibility plugin.
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 WordPress.org
- To avoid losing changes on theme updates, create a child theme. Copy the offending template file (e.g. 'header.php', 'page.php') into your child theme directory.
- Open the file in a code editor (or Appearance → Theme File Editor) and search for '<ul', '<ol', '<li' to locate invalid nesting.
- Correct the markup: only <li> as direct children of <ul>/<ol>, and every <li> must live inside a list container.
- For menus, WordPress outputs standard <ul>/<li> structure via 'wp_nav_menu()'. If a custom Walker class is used (often in 'functions.php'), find the walker and correct the render_list_item / start_el methods to output proper <li> tags.
- Clear caching (plugin or server), then re-audit with the axe DevTools extension.
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 WordPress.org
- Go to Appearance → Theme File Editor in wp-admin.
- Select header.php (or the file that outputs `<head>`) from the right-hand file list.
- Find `<meta name="viewport"` and remove any `maximum-scale` value below 5 and any `user-scalable=no`.
- Save. If the theme auto-regenerates the tag, use a child theme override or a code-snippet plugin to force the corrected tag via the wp_head action.
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 WordPress.org
- In wp-admin go to Appearance → Theme File Editor (or edit files locally via FTP/SSH — recommended for safety).
- Open header.php of your active (child) theme. Verify a skip link exists immediately after <body>: `<a class='skip-link screen-reader-text' href='#primary'>Skip to content</a>`. The exact fragment id varies by theme.
- Find the element in header.php, page.php, or index.php that opens the main content area and confirm it has the matching id (e.g. `<main id='primary' tabindex='-1'>`). Add the id and tabindex if missing.
- If you use a block theme (Full Site Editing), go to Appearance → Editor → Templates → edit the template part that wraps the Query Loop or Page Content block, switch to Code Editor view, and add the id attribute to the <main> tag.
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 WordPress.org
- Same resolution as WooCommerce for the root `lang` attribute: Settings → General → Site Language in the WordPress admin.
- Ensure your active theme's `header.php` uses `<?php language_attributes(); ?>` on the `<html>` tag rather than a hard-coded `lang` value.
- For block themes (Full Site Editing), go to Appearance → Editor → Templates → open the root template and ensure no hard-coded `lang` override is present in any HTML block.
- Verify with the axe browser extension after saving.
Security (OWASP) · 12 fixes
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 WordPress.org
- Install a security headers plugin such as 'Headers Security Advanced & HSTS WP' or 'Solid Security (iThemes Security)' from WordPress Admin → Plugins → Add New.
- In the plugin's settings panel, find the HSTS or Security Headers section and enable Strict-Transport-Security with max-age set to 31536000 and 'Include Subdomains' enabled.
- Alternatively, add the header directly in your theme's functions.php (child theme recommended): `add_action('send_headers', function(){ header('Strict-Transport-Security: max-age=31536000; includeSubDomains'); });`
- For server-level control, edit your Nginx or Apache config as described in the WooCommerce steps above — this is the most reliable method.
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 WordPress.org
- WordPress core does not set HSTS — you must set it at the server or plugin level.
- Plugin method: Install 'Headers Security Advanced & HSTS WP' or 'HTTP Headers' from Plugins → Add New. Activate and navigate to the plugin's settings page. Enable Strict-Transport-Security and set max-age to 31536000 with includeSubDomains checked.
- Apache method: In your root .htaccess, inside a <IfModule mod_headers.c> block add: Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
- Nginx method: In your server {} block for port 443: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; — reload Nginx after saving.
- Verify at securityheaders.com.
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 WordPress.org
- The fix is at the web server layer, not inside WordPress core or a theme.
- For Apache: add `Header unset Server` to your `.htaccess` or virtual host config (requires `mod_headers`). For nginx: add `server_tokens off;` to your `nginx.conf` server block.
- Use the 'HTTP Headers' plugin (Settings → HTTP Headers → Response Headers) to remove the `Server` header without editing server config files directly.
- Verify with browser DevTools (F12 → Network → any request → Response Headers) that `Server` is absent or shows no version string.
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 WordPress.org
- Same stack as WooCommerce. Add `header_remove('X-Powered-By');` to wp-config.php, or use the 'HTTP Headers' or 'Solid Security' (formerly iThemes Security) plugin to suppress the header from the WordPress admin.
- In Solid Security: Admin → Solid Security → Settings → Server Config Tweaks — enable 'Remove File Writing Permissions' and review server header options.
- For Nginx hosting, work with your host to add `more_clear_headers 'X-Powered-By';` in the server block. For Apache, add `Header unset X-Powered-By` to .htaccess.
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 WordPress.org
- Install the 'HTTP Headers' plugin (by RaMMicHaeL) or 'Headers Security Advanced & HSTS WP': WP Admin → Plugins → Add New → search → Install & Activate.
- Go to Settings → HTTP Headers (or the plugin's dashboard), find Content-Security-Policy, enable it, and paste your policy string.
- Alternatively, edit `wp-config.php` or add to `functions.php`: `add_action('send_headers', function(){ header("Content-Security-Policy: default-src 'self'; object-src 'none'"); });`
- If you have server access, add the header to `.htaccess` (Apache) or your Nginx site config as described in the WooCommerce steps above.
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 WordPress.org
- Install a security headers plugin: go to Plugins → Add New, search for 'HTTP Headers' or 'Headers Security Advanced & HSTS WP', install and activate.
- Open the plugin's settings panel and locate the Permissions-Policy (or Feature-Policy) section. Enter the policy value: camera=(), microphone=(), geolocation=(), payment=(), usb=(), fullscreen=(self).
- Save settings. The plugin will add the header to all WordPress responses via PHP's header() function.
- Alternatively, add directly to your theme's functions.php (Appearance → Theme File Editor → functions.php): add_action('send_headers', function(){ header("Permissions-Policy: camera=(), microphone=(), geolocation=()"); });
- Verify with browser DevTools or 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 WordPress.org
- Install a security-headers plugin such as **Headers Security Advanced & HSTS WP** or **WP Headers** from WP Admin → Plugins → Add New.
- Activate the plugin, navigate to its settings page, find the Referrer-Policy option, and set it to `strict-origin-when-cross-origin`.
- Alternatively, add the header directly in `.htaccess` (Apache) or your Nginx server block config as described in the WooCommerce steps above.
- Verify with browser DevTools → Network tab → document response headers.
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 WordPress.org
- WordPress.org (self-hosted) — set HSTS at the server or plugin level, not in WordPress PHP.
- Recommended plugin: Install 'HTTP Headers' by WebFactory from the WordPress plugin directory. Admin → HTTP Headers → Security Headers → Strict-Transport-Security → Enable → set max-age=31536000, check includeSubDomains → Save.
- Server-level (Apache): Add `Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"` inside a `<IfModule mod_headers.c>` block in .htaccess.
- Server-level (Nginx): Add `add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;` in the SSL server block.
- Verify in 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 WordPress.org
- Install the 'HTTP Headers' or 'Security Headers' plugin from WP Admin → Plugins → Add New.
- Configure it to send X-Content-Type-Options: nosniff on all responses.
- Alternatively, add the header via your server config: in .htaccess (Apache) add 'Header always set X-Content-Type-Options "nosniff"'; in nginx.conf add 'add_header X-Content-Type-Options nosniff always;' inside the server {} block.
- Or use the functions.php method: add_action('send_headers', function(){ header('X-Content-Type-Options: nosniff'); }); in Appearance → Theme File Editor → functions.php.
- Confirm the header appears in browser dev-tools Network tab after saving.
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 WordPress.org
- SPF is set at your DNS/registrar, not in WordPress itself.
- Log in to your hosting control panel (cPanel → 'Zone Editor' or 'Email Deliverability') or your external DNS provider.
- Find the TXT record for @ starting with v=spf1.
- If you use an SMTP plugin (WP Mail SMTP, Fluent SMTP, Easy WP SMTP) connected to SendGrid, Mailgun, Brevo, etc., include that provider's SPF include value before adding -all.
- Save and test with an SPF checker.
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 WordPress.org
- Install the 'HTTP Headers' plugin or 'Solid Security' (formerly iThemes Security) from WordPress Admin → Plugins → Add New.
- Solid Security path: WordPress Admin → Security → Settings → Security Check → or search 'Security Headers' within the plugin — enable `X-Content-Type-Options: nosniff`.
- If your host (e.g. WP Engine, Kinsta, SiteGround) already injects this header at the server level, do NOT also add it via plugin — check your host's dashboard or contact support to confirm.
- Verify with DevTools or securityheaders.com.
X frame options weakQuick win
Change the X-Frame-Options response header from its current weak or missing value to either DENY or SAMEORIGIN so your store cannot be embedded in a malicious iframe.
On WordPress.org
- Install the 'HTTP Headers' plugin (by David Gwyer) or 'Headers Security Advanced & HSTS WP' from the WordPress plugin repository.
- In WordPress admin, navigate to the plugin's settings page, locate X-Frame-Options, select DENY (or SAMEORIGIN), and save.
- Without a plugin, add to your active theme's functions.php or a must-use plugin: add_action('send_headers', function(){ header('X-Frame-Options: DENY'); });
- Apache server: open wp-content or root .htaccess and add inside the <IfModule mod_headers.c> block: Header always set X-Frame-Options "DENY"
- Nginx server: add add_header X-Frame-Options "DENY" always; inside the server {} block in your site config, then sudo nginx -s reload.
- Confirm with DevTools → Network → select the HTML document → Response Headers.
Site Lifecycle · 4 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 WordPress.org
- Log in to WordPress Admin → Dashboard → Updates to see your current WordPress version and any available updates.
- Apply core, plugin, and theme updates from this single screen.
- Go to Dashboard → Updates → Automatic Updates to enable automatic minor core updates.
- Check that your PHP version (visible under Tools → Site Health → Info → Server) meets the minimum required by the latest WordPress release.
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 WordPress.org
- Install the 'GTM4WP' plugin: WP Admin → Plugins → Add New → search 'GTM4WP' → Install & Activate.
- Navigate to Settings → Google Tag Manager, enter your GTM Container ID, choose where to inject the tag (recommended: 'Footer (below wp_footer hook)' for performance), and Save.
- For WooCommerce stores, enable ecommerce tracking in GTM4WP Settings → Integration tab → Track eCommerce → Save.
- In GTM, set up GA4 Configuration tag and GA4 Event tags for the dataLayer events pushed by GTM4WP. Publish.
- Verify with GTM Preview mode and GA4 DebugView.
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 WordPress.org
- Go to Settings › General in your WordPress admin and set the Site Language dropdown to your store's primary language — this is the master control.
- WordPress's language_attributes() function in your theme's header.php will then automatically output the correct lang attribute.
- If your theme has hard-coded the <html> tag without language_attributes(), go to Appearance › Theme File Editor › header.php and replace the opening tag with: <html <?php language_attributes(); ?>>
- Save changes and verify via View Page Source.
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 WordPress.org
- The fix is identical to WooCommerce above — WordPress.org (self-hosted) shares the same Apache/Nginx server layer.
- Via .htaccess (Apache): add `Header unset Server` and `Header unset X-Powered-By` and `ServerSignature Off` to your root .htaccess.
- Via plugin: install 'HTTP Headers' or 'Security Headers' plugin (Dashboard → Plugins → Add New), activate, and configure removal of Server and X-Powered-By headers.
- For PHP specifically: in wp-config.php or a must-use plugin, you can add `header_remove('X-Powered-By');` before any output is sent. To suppress PHP version exposure also set `expose_php = Off` in php.ini if your host allows it.
- Contact your host for server-level (ServerTokens) changes if you are on shared hosting without root access.