Description
Fine-grained control over how search engines index and display your WordPress content. Apply 5 independent robots directives to 22 different page contexts with flexible implementation methods.
5 Robots Directives:
- noindex: Prevent search engines from indexing the page
- nofollow: Prevent search engines from following links on the page
- noarchive: Prevent search engines from showing cached versions
- nosnippet: Prevent search engines from showing text snippets in results
- noimageindex: Prevent search engines from indexing images on the page
Implementation Methods:
- HTML Meta Tags: Traditional method, easy to verify in page source (default)
- HTTP Headers: More robust, works with all content types including PDFs and images
- Both: Maximum compatibility for all scenarios
Control Levels:
- Global Settings: Apply directives to 22 different page contexts (posts, pages, archives, etc.)
- Granular Control (Optional): Override global settings for individual posts, pages, and custom post types via meta boxes in the editor
Perfect for:
- Blocking indexing of attachment pages while allowing link following
- Preventing duplicate content issues with flexible directive combinations
- Controlling archive page indexing with granular control
- Managing pagination SEO with independent settings
- Protecting private content from search engine caching
- Preventing snippet display while still indexing content
Main pages
- Front Page: Block the indexing of the site’s front page.
- Home: Block the indexing of the site’s home page.
Pages and Posts
- Page: Block the indexing of the site’s pages.
- Privacy Policy: Block the indexing of the site’s privacy policy page.
- Single: Block the indexing of a post on the site.
- Singular: Block the indexing of a post or a page of the site.
Taxonomies
- Category: Block the indexing of the site categories. The lists where the posts appear.
- Tag: Block the indexing of the site’s tags. The lists where the posts appear.
Dates
- Date: Block the indexing when any date-based archive page (i.e. a monthly, yearly, daily or time-based archive) of the site. The lists where the posts appear.
- Day: Block the indexing when a daily archive of the site. The lists where the posts appear.
- Month: Block the indexing when a monthly archive of the site. The lists where the posts appear.
- Time: Block the indexing when an hourly, “minutely”, or “secondly” archive of the site. The lists where the posts appear.
- Year: Block the indexing when a yearly archive of the site. The lists where the posts appear.
Archives
- Archive: Block the indexing of any type of Archive page. Category, Tag, Author and Date based pages are all types of Archives. The lists where the posts appear.
- Author: Block the indexing of the author’s page, where the author’s publications appear.
- Post Type Archive: Block the indexing of any post type page.
Pagination
- Pagination: Block the indexing of the pagination, i.e. all pages other than the main page of an archive.
Search
- Search: Block the indexing of the internal search result pages.
Attachments
- Attachment: Block the indexing of an attachment document to a post or page. An attachment is an image or other file uploaded through the post editor’s upload utility. Attachments can be displayed on their own “page” or template. This will not cause the indexing of the image or file to be blocked.
Previews
- Customize Preview: Block the indexing when a content is being displayed in customize mode.
- Preview: Block the indexing when a single post is being displayed in draft mode.
Error Page
- Error 404: This will cause an error page to be blocked from being indexed. As it is an error page, it should not be indexed per se, but just in case.
Important note: if you have any doubt about any of the following items it is best not to activate the option as you could lose results in the search engines.
Compatibility
- WordPress: 5.7 – 7.1
- PHP: 7.2 – 8.5
Security
This plugin adheres to the following security measures and review protocols for each version:
- WordPress Plugin Handbook
- WordPress Plugin Security
- WordPress APIs Security
- WordPress Coding Standards
- Plugin Check (PCP)
Privacy
- This plugin does not collect any information about your site, your identity, the plugins, themes or content the site has.
Vulnerabilities
- No vulnerabilities have been published up to version 2.0.1.
- Version 2.0.0 includes proactive security hardening based on comprehensive security audit (see docs/SECURITY-2026-01-20.md).
Found a security vulnerability? Please report it to us privately at the noindex SEO GitHub repository.
Installation
Automatic download
Visit the plugin section in your WordPress, search for [noindex-seo]; download and install the plugin.
Manual download
Extract the contents of the ZIP and upload the contents to the /wp-content/plugins/noindex-seo/ directory. Once uploaded, it will appear in your plugin list.
Reviews
Contributors & Developers
“noindex SEO” is open source software. The following people have contributed to this plugin.
Contributors“noindex SEO” has been translated into 3 locales. Thank you to the translators for their contributions.
Translate “noindex SEO” into your language.
Interested in development?
Browse the code, check out the SVN repository, or subscribe to the development log by RSS.
Changelog
3.1.1 [2026-07-18]
Security
- Tightened the ACL on the two REST endpoints introduced in 3.1.0. The original release used
__return_truefor bothpermission_callbacks, which would have leaked the full noindex configuration to anyone hitting/wp-json/noindex-seo/v1/settings. Fixed callbacks:/settingsnow requiresmanage_options. The consolidated array reveals the site’s full SEO strategy in a single response, so it is admin-only./effective?post_id=Nis public only for posts withpost_status=publishand no password (the directives for those would appear in the HTML<meta>tag of the rendered page anyway). Drafts, private posts, scheduled posts, password-protected posts, and trashed posts requireedit_postcapability for the specific post.
Compatibility
- WordPress: 5.7 – 7.1
- PHP: 7.2 – 8.5
Tests
- Added 4 tests covering the new permission model: unauthenticated rejected on
/settings, editor rejected on/settings, unauthenticated/subscriber rejected on/effectivefor drafts, public access preserved on/effectivefor published posts.
3.1.0 [2026-07-18]
Closes the three remaining HIGH-priority items from the original gap audit.
Added
- WP core XML sitemap integration. Hooks
wp_sitemaps_posts_query_args,wp_sitemaps_taxonomies_query_args, andwp_sitemaps_users_query_argsso URLs that should not be indexed drop out of/wp-sitemap.xml. Two layers of exclusion: (1) whole-type exclusion — ifsingular.noindex=1hides every post type;single.noindex=1hides all posts;page.noindex=1hides all pages;attachment.noindex=1hides all attachments;cpt_{type}.noindex=1hides that CPT;category.noindex=1/tag.noindex=1hide those taxonomies;author.noindex=1hides the entire users sitemap. (2) per-item exclusion — posts/terms with the granular_noindex_seo_override=1+_noindex_seo_noindex=1flag are excluded from their type’s sitemap. Closes the biggest SEO-quality gap (Google Search Console flags noindexed URLs in sitemaps as a coverage warning). - Per-CPT global defaults. Every public custom post type (anything public + non-built-in: WooCommerce
product, EDDdownload, LMS lesson/course, etc.) now gets its own contextcpt_{post_type}and a “Custom Post Types” section in the settings page. Previously every CPT single fell into the genericsinglebucket; now each CPT can have its own noindex / nofollow / noarchive / nosnippet / noimageindex defaults that apply globally to all of its items. - REST API exposure. Two new read-only endpoints under
/wp-json/noindex-seo/v1/:GET /settings— returns the consolidated settings array (contexts × directives + config), so headless consumers (Next.js, Gatsby, mobile apps) can read the global configuration.GET /effective?post_id=123— returns the active directive list for a specific post, computed by the same precedence used on the front-end (per-post override per-CPT default built-insingle/page/attachmentcontext). Includes the implementationmethodso consumers know whether to render a<meta>tag, anX-Robots-Tagheader, or both.
Compatibility
- WordPress: 5.7 – 7.1
- PHP: 7.2 – 8.5
Tests
- Added
tests/test-sitemap.php(per-post + global-context exclusion for posts/terms/users sitemaps). - Added
tests/test-rest-api.php(settings endpoint, effective endpoint for post-meta and CPT-default sources, 404 for unknown post). - Added
tests/test-cpt-contexts.php(dynamic context registration, per-CPT settings application).
3.0.4 [2026-07-18]
Changed
- Moved the “Apply Recommended Defaults” button from above the Search Results section to immediately above the “Search options…” input box. The previous position pushed the button below the fold on smaller screens; the new position sits it next to the search field where users naturally look for global actions.
Compatibility
- WordPress: 5.7 – 7.1
- PHP: 7.2 – 8.5
3.0.3 [2026-07-18]
Added
- “Robots” column on taxonomy list tables. When per-term granular control is on, the Categories / Tags / custom taxonomy list pages now show a “Robots” column with the same directive badges (🔍 noindex, 🔗 nofollow, 💾 noarchive, 📄 nosnippet, 🖼️ noimageindex) used on the post list. Makes it possible to scan at a glance which terms have an override and which directives are active — parity with the existing post-list UX.
Compatibility
- WordPress: 5.7 – 7.1
- PHP: 7.2 – 8.5
Tests
- Added 3 tests covering the term column renderer (no override, with override, passthrough for other columns).
3.0.2 [2026-07-18]
Fixed
- Duplicate “Search Engine Visibility” panel in the block editor. The plugin previously registered both a classic PHP meta box (
add_meta_box) AND a native Gutenberg sidebar panel (PluginDocumentSettingPanelviaassets/js/editor-sidebar.js) with the same title — both showed up in the block editor sidebar as two panels with the same name. The classic meta box is now suppressed on block-editor screens (detected viaget_current_screen()->is_block_editor, which respects the Classic Editor plugin’s per-request override). Classic editor users still get the meta box.
Added
- Per-term granular control for taxonomies. Mirrors the existing per-post granular pattern: when “Enable per-term granular control” is on, every public taxonomy (
category,post_tag, and any custom public taxonomy like WooCommerce’sproduct_cat/product_tag) gets a “Search Engine Visibility” panel on the Edit Tag screen with an override checkbox and the five directive checkboxes. Term-level directives take precedence over the globalcategory/tag/post_type_archivesettings when the term has the override enabled. Backed byregister_term_meta()withshow_in_rest=trueso headless consumers can read them.
Compatibility
- WordPress: 5.7 – 7.1
- PHP: 7.2 – 8.5
Tests
- Added
tests/test-term-meta.phpcovering registration, save handler, front-end override precedence, and uninstall cleanup.
3.0.1 [2026-07-18]
Fixed
- Self-healing v3 migration. If
noindex_seo_config_versionclaims migration completed (version >= 3) but the consolidatednoindex_seo_settingsoption is somehow missing,noindex_seo_check_migration()now re-runs the migration automatically. This recovers silently from partial failures: object-cache staleness after the write, request interrupted mid-migration, a third-party plugin clearing options, or DB write errors that did not raise a PHP error. Without this guard, the front-end would fall back to all-zero defaults (site fully indexable) until someone re-saved the settings page.
Compatibility
- WordPress: 5.7 – 7.1
- PHP: 7.2 – 8.5
Tests
- Added test for the self-healing migration path.
3.0.0 [2026-07-18]
Major release — storage consolidation. Settings are now stored in a single autoloaded option (noindex_seo_settings) instead of ~110 individual options. Reduces wp_options autoload weight on every page load. Legacy options are preserved (marked non-autoloaded) for one major version to allow safe rollback to 2.x; they will be removed in 4.0.
Added
- “Apply recommended defaults” button on the settings page. One click enables all 12 contexts flagged as “Recommended” in the UI (privacy policy, date archives, pagination, search results, attachment pages, preview/customize-preview, and 404). Previously required ~60 individual checkbox toggles.
- Filterable conflict-detection list. The list of plugins that may conflict with noindex SEO is now passed through
apply_filters('noindex_seo_conflicting_plugins', ...). Third-party code can extend or replace the list without editing the plugin.
Changed
- Storage consolidated: 110 directive options (
{directive}_seo_{context}) + 5 config options (noindex_seo_config_*) merged into onenoindex_seo_settingsarray. Reads go through new accessors (noindex_seo_get_settings(),noindex_seo_get_setting(),noindex_seo_get_config()). The hourly transient cache is preserved on top. - Transient invalidation is now automatic: any update to
noindex_seo_settingsclears thenoindex_seo_optionstransient via theupdate_option_noindex_seo_settingshook. Previously only the admin form save cleared it, leaving WP-CLI / REST / programmatic edits stale for up to one hour. - Migration: existing 2.x installs are migrated automatically on
plugins_loaded(idempotent, version-gated bynoindex_seo_config_version). Legacy options are kept and marked non-autoloaded so a rollback to 2.x restores the pre-3.0 state. - Uninstall retrocompatibility:
uninstall.phpdeletes both the new consolidated option and all legacy individual options (via wildcard SQL). Sites that upgrade from 2.x and later uninstall will be cleaned up completely regardless of whether migration ran.
Compatibility
- WordPress: 5.7 – 7.1
- PHP: 7.2 – 8.5
Tests
- Added PHPUnit test suite (previously the project shipped only a plugin-header smoke test). Coverage: contexts helper, settings accessors, v2v3 migration, form save, bulk actions, meta box save, conflict-detection filter.
2.0.2 [2026-07-18]
Fixed
- Bulk action “Enable Robots Override” no longer creates duplicate
postmetarows. The previous implementation usedINSERT ... ON DUPLICATE KEY UPDATE, but WordPress’spostmetatable has no unique key on(post_id, meta_key), so theON DUPLICATE KEY UPDATEbranch never fired and every bulk run on a post that already had the meta inserted a duplicate row. The bulk handler now usesupdate_post_meta()anddelete_post_meta()exclusively. - Bulk action “Disable Robots Override” now removes the override meta entirely (previously set its value to
0, leaving a redundant row).
Changed
- Removed three dead contexts (
feed,comment_feed,robots) that were registered, migrated, and cleaned up but never evaluated at runtime and never exposed in the admin UI. The plugin now consistently registers and evaluates 22 page contexts (down from a nominal 25). Existing orphan options from earlier installs are cleaned by the uninstall routine’s wildcard delete. - Extracted
noindex_seo_get_contexts()as the single source of truth for the context list shared by settings registration, v2 migration, form processing.uninstall.phpretains its own copy (technical constraint: the main plugin file is not loaded during uninstall) with a sync warning comment. - Corrected
phpcs.xmlminimum_supported_wp_versionfrom6.6to5.7to match the plugin header. This setting was stale from before the 2.0.1 minimum-WP correction. - Updated
readme.txtdescription counts from 25 to 22 page contexts. - Bulk action performance characteristic changed: the rewrite from a single batched SQL statement to per-post
update_post_meta()/delete_post_meta()calls trades O(1) queries for O(N) queries on bulk selections, in exchange for correctness (no more duplicate rows) and proper WP meta-cache invalidation. Typical admin bulk selections (≤200 posts) are unaffected; very large selections (thousands of posts) may take noticeably longer.
Deprecated
noindex_seo_clear_transient(): was hooked toupdate_option_noindexseo, a hook that never fires becausenoindexseois the Settings API group name, not an option name. The function now emits a deprecation notice via_deprecated_function()and continues to clear thenoindex_seo_optionstransient if called directly. The transient is cleared correctly insidenoindex_seo_process_form()after every successful save. The function will be removed in a future release.
Code Quality
bin/preflight.sh: hardened temp-file handling (mktemp moved to script top,trap … EXITcleanup, no more predictable/tmp/composer-audit.*paths that were vulnerable to symlink overwrite on shared hosting); fixed2>/nulltypo; added rsync /.distignore/ build-tree-non-empty guards so the forbidden-artefact scan cannot pass vacuously; replaced npm-audit subshell withpushd/popdso gate counters are not lost.- Added
bin/preflight.sh: automated pre-deploy verification covering PHPCS, PHPStan level 9, PHPCompatibility, PHPUnit, dependency audit, and candidate-ZIP artefact inspection. Prints a PASS/FAIL report per section and exits non-zero on any failure. - Added
.claude/settings.jsondeny rules fordeploy.sh,git push,git tag, andgit merge, enforcing the agent operating boundaries mandated by AGENTS.md mechanically rather than only in prose.
Compatibility
- WordPress: 5.7 – 7.1
- PHP: 7.2 – 8.5
Tests
- PHP Coding Standards: 3.13.5
- WordPress Coding Standards: 3.3.0
- PHPStan: 2.1.55
- PHPUnit: 9.6.34
2.0.1 [2026-05-25]
Added
- New option “Delete all plugin data on uninstall” (Settings noindex SEO General Configuration). By default, all data is preserved when the plugin is uninstalled. Enable this option to remove all settings and per-post directives on uninstall.
Changed
- Lowered minimum WordPress requirement from 6.6 to 5.7.
- Custom post types registered by plugins or themes on the
inithook now correctly receive the Robots column, bulk actions, and list filter in granular control mode.
Code Quality
- Added
NOINDEX_SEO_VERSIONconstant for asset cache-busting. - PHP type safety improvements; passes PHPStan level 9 on PHP 8.5 with zero errors.
- Extracted
noindex_seo_save_directives_from_post()helper to eliminate duplicated save logic. - Removed dead code and dead hook registration.
Compatibility
- WordPress: 5.7 – 7.1
- PHP: 7.2 – 8.5
Tests
- PHP Coding Standards: 3.13.5
- WordPress Coding Standards: 3.3.0
- PHPStan: 2.1.55
- PHPUnit: 9.6.34
2.0.0 [2026-01-20]
New Features
- 5 independent robots directives: noindex, nofollow, noarchive, nosnippet, noimageindex
- Each directive can be enabled independently for any page context (125 total options)
- HTTP Headers support: Choose between HTML meta tags, HTTP headers, or both
- Granular per-post/page control (optional):
- Override global settings for individual posts/pages
- Meta boxes for Classic Editor
- Native Gutenberg sidebar panel for Block Editor
- Quick Edit support for fast inline editing
- Bulk actions to enable/disable overrides
- Custom “Robots” column in post lists
- Filter posts by override status
- Modern checkbox-based interface with emoji icons and tooltips
Security & Code Quality
- Modernized code with PHP 7.2+ strict types and type declarations
Compatibility & Migration
- WordPress: 5.7 – 7.1
- PHP: 7.2 – 8.5
- Automatic migration from v1.x (your existing settings are preserved)
- No manual configuration needed – just update and go
- Fully backward compatible
1.2.0 [2025-04-08]
Changes
- Improved functions documentation.
Fixes
- The way the options are saved.
Compatibility
- WordPress: 4.1 – 6.8
- PHP: 5.6 – 8.4
Tests
- PHP Coding Standards: 3.12.1
- WordPress Coding Standards: 3.1.0
- Plugin Check (PCP): 1.4.0
1.1.1 [2024-11-04]
Added
- Configuration option to dismiss other SEO plugin incompatibilities.
Compatibility
- WordPress: 4.1 – 6.7
- PHP: 5.6 – 8.4
1.1.0 [2024-11-02]
Added
- Detects other WordPress SEO plugins, and creates a notice about it, to avoid conflicts.
- Has filters, so other plugins can hack.
Changed
- Uses native wp_robots functions (since WP 5.7+)
- Big refactory.
- Less size, improved code quality.
Compatibility
- WordPress: 4.1 – 6.7
- PHP: 5.6 – 8.4
Tests
- PHP Coding Standards: 3.10.3
- WordPress Coding Standards: 3.1.0
- Plugin Check (PCP): 1.1.0
