Signocore SEO passes its output through WordPress filters, so a theme or a small plugin can adjust titles, meta tags, the schema graph, sitemaps, breadcrumbs, robots.txt and the global scripts without editing the plugin. Add callbacks with add_filter() from your theme's functions.php or a must-use plugin. Every hook name starts with wpsseo_, and the plugin fires no actions of its own.
Much of this output is cached, and a filter only runs when its cache is rebuilt: the head output for a week, breadcrumb trails for an hour, sitemaps for a day and the sitemap stylesheet for 30 days. Each group below says when its filters run and what clears the cache. While you work on a callback, saving any Signocore SEO settings page clears the cached head output and the sitemaps.
Return the type listed for each filter. Most values go straight into typed plugin code, where a wrong type such as null causes a fatal error. Hooks renamed in 6.0.0 still work under their old names and trigger a deprecation notice when WP_DEBUG is on.
No hooks match "".
Meta tags
These filters shape what Signocore SEO prints at the top of wp_head, and they run inside it, so conditional tags such as is_singular() work. The head output is cached for a week per post, term, author or home page, with separate variants for each language, year, page number and breadcrumb trail, so apart from the <title> element these filters only run when that cache is rebuilt. Saving a post, term or user profile clears its own entry, and saving any Signocore SEO settings page or updating the plugin clears them all. Search results, date archives and post type archives are never cached, and neither is anything on a site set to discourage search engines. 404 pages get no head output, only the title.
wpsseo_meta_doctitle
filterFilters the document title used for the <title> element, og:title and twitter:title.
The title can already carry the company name and, on paginated archives, a page number, both joined with •, and it can contain other HTML entities. Return plain text: the result is escaped for output, and existing entities are kept. The <title> element is built on every request through core's pre_get_document_title filter, so a change shows there at once, while og:title and twitter:title keep the cached value until the head output is rebuilt. The filter can run twice in one request. Return an empty string to let WordPress build its own <title> and drop the two social title tags. Not applied to feeds.
| Parameter | Type | Description |
|---|---|---|
$title |
string |
The document title. Can contain HTML entities such as •. |
Returns string The title as plain text.
add_filter('wpsseo_meta_doctitle', function (string $title): string { // Use a pipe instead of the bullet before the company name and the page number. return str_replace(' • ', ' | ', $title); });
wpsseo_meta_description
filterFilters the meta description used for the description, og:description and twitter:description tags.
The value comes from the SEO box, a description template, the home page settings or an automatic description, with placeholders resolved. Paginated archives get the page number appended. Return an empty string to print none of the three tags. Post type archives and date archives have no description of their own, and since they are never cached, the filter runs on every request there.
| Parameter | Type | Description |
|---|---|---|
$description |
string |
The meta description. Can be empty. |
Returns string The meta description, or an empty string for none.
add_filter('wpsseo_meta_description', function (string $description): string { if ($description === '' && is_post_type_archive('event')) { return 'Upcoming workshops, webinars and meetups, with dates, venues and tickets.'; } return $description; });
wpsseo_meta_canonical_url
filterFilters the canonical URL used for <link rel="canonical"> and og:url.
The value already includes a custom canonical URL from the SEO box, and a trailing slash when the permalink structure ends with one. Paginated archives point to their own page. Return an empty string to print neither tag.
| Parameter | Type | Description |
|---|---|---|
$url |
string |
The canonical URL. |
Returns string The canonical URL, or an empty string for none.
add_filter('wpsseo_meta_canonical_url', function (string $url): string { // Point canonical URLs on the staging copy at the live site. return str_replace('https://staging.example.com', 'https://example.com', $url); });
wpsseo_meta_social_image
filterFilters the image used for the Open Graph and Twitter image tags.
The plugin takes the custom social image of the post, term or author, then the post's featured image, then the Default Social Image from the settings, in the 1200 x 630 social-image size. Return an empty array to print no image tags, which also drops twitter:card. width and height are only printed when they are above zero.
| Parameter | Type | Description |
|---|---|---|
$image |
array |
An array with the keys url (string), width (int), height (int) and alt (string), or an empty array when no image was found. The alt text falls back to the attachment title. |
Returns array The image with at least url, or an empty array for no image.
add_filter('wpsseo_meta_social_image', function (array $image): array { if (!is_singular('post')) { return $image; } $postId = get_queried_object_id(); // An image rendered for each post by an external service. return [ 'url' => 'https://images.example.com/og/' . $postId . '.png', 'width' => 1200, 'height' => 630, 'alt' => wp_strip_all_tags(get_the_title($postId)), ]; });
wpsseo_meta_blog_name
filterFilters the site name used for og:site_name.
Receives the Site Title from Settings > General, which WordPress stores HTML-escaped. It does not change the company name used in titles and in the schema graph.
| Parameter | Type | Description |
|---|---|---|
$blogName |
string |
The site title. |
Returns string The site name as plain text.
add_filter('wpsseo_meta_blog_name', function (string $blogName): string { // The site title is "Example Store | Handmade leather goods"; share only the brand. return 'Example Store'; });
wpsseo_meta_tags
filterFilters the block of meta and link tags that the plugin prints in the head.
The string holds the description, canonical, author, prev and next, prefetch, Open Graph, Twitter, Facebook app and site verification tags, one per line, and the result is what gets cached. The plugin appends its schema.org JSON-LD <script> through this same filter at priority 10, so a callback at a lower priority sees the tags without the schema, and one above 10 sees both. The opening and closing comments are added outside this filter.
| Parameter | Type | Description |
|---|---|---|
$metaTags |
string |
The tags as HTML. |
Returns string The tags to print, as HTML.
add_filter('wpsseo_meta_tags', function (string $metaTags): string { // Drop the prefetch hints for the previous and next page on paginated archives. return (string) preg_replace('/<link rel="prefetch" href="[^"]*">\n/', '', $metaTags); });
wpsseo_meta_comment_block
filterFilters the HTML comments that open and close the plugin's head output.
Fires twice each time the head output is built: first with <!-- Signocore SEO -->, then with <!-- Signocore SEO End -->, each followed by a newline. Check the value to change only one of them, and return an empty string to remove a comment.
| Parameter | Type | Description |
|---|---|---|
$comment |
string |
The HTML comment, including the trailing newline. |
Returns string The comment to print, or an empty string for none.
add_filter('wpsseo_meta_comment_block', function (string $comment): string { // Remove both the opening and the closing comment. return ''; });
Schema
The schema.org graph is printed as one JSON-LD script with the head output, so these filters run when the head cache is rebuilt (see Meta tags). The graph is only printed once the Schema or Social settings page has been saved at least once. Both filters work without a license, but some of the nodes they deal with need Pro.
wpsseo_schema_page_types
filterFilters the schema.org types of the current page, which set the @type of the page node and decide some of the other nodes in the graph.
On single posts the list holds WebPage plus Article, Product or, with Pro, FAQPage where they apply, and the page type chosen in the SEO box, such as ContactPage. Author archives get WebPage and ProfilePage, the blog home WebPage and CollectionPage, and other archives start with CollectionPage. Article and Product are taken out of the page node and produce a separate Article or Product node instead. Article and ProfilePage add the author's Person node, and ProfilePage and CollectionPage add an ItemList of the posts in the main query. The Product, Person and ItemList nodes need Pro. Entries that are not strings are dropped. The filter runs several times while one graph is built, so return the same result for the same page.
| Parameter | Type | Description |
|---|---|---|
$pageTypes |
string[] |
schema.org type names. |
Returns string[] The type names for the page.
add_filter('wpsseo_schema_page_types', function (array $pageTypes): array { if (function_exists('is_checkout') && is_checkout()) { $pageTypes[] = 'CheckoutPage'; } return $pageTypes; });
wpsseo_schema_graph
filterFilters the complete list of schema.org nodes before they are printed as one JSON-LD @graph.
Receives the nodes as a list of arrays without @context: WebSite, Organization, the site navigation, BreadcrumbList, the primary image, the page node and Article, plus, with Pro, Product, Person, ItemList, FAQ questions, advanced types such as Recipe or Event, the glossary term set and the custom JSON-LD from the SEO box. Nodes refer to each other by @id, such as https://example.com/#organization. Entries that are not arrays are dropped, and the rest is printed in one <script type="application/ld+json"> with @context added. When no nodes are left, no script is printed.
| Parameter | Type | Description |
|---|---|---|
$schema |
array[] |
The graph nodes, each an array such as ['@type' => 'WebSite', '@id' => '...', ...]. |
Returns array[] The nodes to print.
add_filter('wpsseo_schema_graph', function (array $schema): array { foreach ($schema as $index => $node) { if (str_ends_with((string) ($node['@id'] ?? ''), '#organization')) { $schema[$index]['foundingDate'] = '2015-03-01'; } } return $schema; });
Sitemap
Sitemaps are generated early in the request, on send_headers, before WordPress runs the main query, so conditional tags do not apply here. Each sitemap page is cached for a day per locale, and the XSL stylesheet that styles the sitemaps in a browser for 30 days. Saving a post or any Signocore SEO settings page clears every sitemap and the stylesheet, saving a term clears the sitemap index and that taxonomy's sitemaps, and saving a user profile clears the index and the author sitemap. Updating the plugin clears them all.
wpsseo_sitemap_url
filterFilters every URL written to the XML sitemaps: sitemap index entries, page URLs and image URLs.
The URL arrives escaped for XML (& as &) and is written into <loc> or <image:loc> exactly as returned, so keep it XML-safe. The front_page entry is only added to the page sitemap when no static front page is set, so its $objectId is always null.
| Parameter | Type | Description |
|---|---|---|
$url |
string |
The URL, escaped for XML. |
$type |
string |
What the URL points to: sitemap_index for an entry in the sitemap index, a post type name such as post, page or product, term, author, front_page, or attachment for an image. |
$objectId |
int|null |
The post ID, term ID or user ID. null for sitemap_index, front_page and attachment. |
Returns string The URL, escaped for XML.
add_filter('wpsseo_sitemap_url', function (string $url, string $type, ?int $objectId): string { // Serve image URLs from the CDN. if ($type === 'attachment') { return str_replace('https://example.com/wp-content/uploads/', 'https://cdn.example.com/uploads/', $url); } return $url; }, 10, 3);
wpsseo_sitemap_excluded_urls
filterFilters the IDs of posts left out of the post type sitemaps.
Despite its name, this filter works with post IDs, not URLs. The list starts with every post marked noindex in the SEO box plus the WooCommerce pages from wpsseo_woocommerce_page_ids, and the result goes to the post type sitemap queries as post__not_in. It does not change the robots meta of those posts, and it does not affect term or author sitemaps. The filter only runs when a post type sitemap is built, so conditional tags do not apply. Changes show when a sitemap is rebuilt.
| Parameter | Type | Description |
|---|---|---|
$postIds |
int[] |
Post IDs, as integers. |
Returns int[] The post IDs to leave out.
add_filter('wpsseo_sitemap_excluded_urls', function (array $postIds): array { // The newsletter thank-you page and a campaign landing page. return array_merge($postIds, [128, 256]); });
wpsseo_sitemap_author_url
filterFilters the URL of an author archive in the author sitemap.
Receives the URL from get_author_posts_url() before it is escaped, so return a plain URL. The result then passes through wpsseo_sitemap_url with the type author. The author sitemap lists authors with published posts of the post type, except authors marked noindex, and is left out entirely when author archives are set to noindex.
| Parameter | Type | Description |
|---|---|---|
$url |
string |
The author archive URL. |
$authorId |
int |
The author's user ID. |
Returns string The URL to list, not escaped.
add_filter('wpsseo_sitemap_author_url', function (string $url, int $authorId): string { // List the team member's profile page instead of the author archive. $profilePageId = (int) get_user_meta($authorId, 'profile_page_id', true); $profileUrl = $profilePageId > 0 ? get_permalink($profilePageId) : false; return $profileUrl ?: $url; }, 10, 2);
wpsseo_sitemap_accent_color
filterFilters the accent color of the stylesheet that styles the sitemaps in a browser.
Only changes how the sitemaps look to people, since search engines ignore the stylesheet. Return a hex color such as #c4782e, without quotes; any other value falls back to the default. The stylesheet is cached for 30 days, and saving a post, saving any Signocore SEO settings page or updating the plugin clears it.
| Parameter | Type | Description |
|---|---|---|
$color |
string |
A hex color. Default #546e7a. |
Returns string A hex color.
add_filter('wpsseo_sitemap_accent_color', function (string $color): string { return '#c4782e'; });
Breadcrumbs
Breadcrumbs print through the [signocore-breadcrumbs] shortcode or a theme call to \Signocore\Features\Breadcrumbs::render(), never in the admin or on 404 pages. The trail is cached per URL for an hour. It also feeds the BreadcrumbList node in the schema graph and keys the head cache, so the trail is built on every page view that misses its cache, even when the theme prints no breadcrumbs. The two display filters run on every render.
wpsseo_breadcrumb_args
filterFilters the HTML that wraps and separates the breadcrumbs.
The keys are wrap_start and wrap_end around the whole trail, separator between items, and item_wrap_start and item_wrap_end around each item. Every call receives the defaults, the values are printed without escaping, and the result is merged over the defaults, so you can return only the keys you change. Anything other than an array is ignored.
| Parameter | Type | Description |
|---|---|---|
$args |
array |
Display arguments. By default wrap_start is <div class="breadcrumbs">, wrap_end is </div>, separator is <span class="sep" aria-hidden="true">›</span> with a space on each side, and both item wraps are empty. |
Returns array The display arguments, as HTML strings.
add_filter('wpsseo_breadcrumb_args', function (array $args): array { $args['wrap_start'] = '<nav class="breadcrumbs" aria-label="Breadcrumb">'; $args['wrap_end'] = '</nav>'; $args['separator'] = ' <span class="sep" aria-hidden="true">/</span> '; return $args; });
wpsseo_breadcrumb_structure
filterFilters the breadcrumb trail before it is cached, printed and added to the schema graph.
The first crumb is the home page and the last is the current page. On the front page the trail is empty. The plugin runs URLs through esc_url() and labels through esc_html(), and labels are decoded and stripped of tags before output, so plain text works too. The result is cached per URL for an hour, except on search results, so base changes on the requested page, not on the visitor. Saving a post, term or user profile clears its trail.
| Parameter | Type | Description |
|---|---|---|
$breadcrumbs |
array[] |
The crumbs in order, each an array with url (string), label (string) and add_link (bool, whether the crumb is printed as a link). |
Returns array[] The crumbs, each with url, label and add_link.
add_filter('wpsseo_breadcrumb_structure', function (array $breadcrumbs): array { if (!is_singular('event') || count($breadcrumbs) < 2) { return $breadcrumbs; } $archiveUrl = get_post_type_archive_link('event'); if ($archiveUrl === false) { return $breadcrumbs; } // Home > Events > Event title array_splice($breadcrumbs, -1, 0, [[ 'url' => esc_url($archiveUrl), 'label' => 'Events', 'add_link' => true, ]]); return $breadcrumbs; });
wpsseo_breadcrumb_links
filterFilters the rendered breadcrumb items before they are joined with the separator.
Each item is HTML: an <a> link for crumbs with add_link, the escaped label for the others, wrapped in item_wrap_start and item_wrap_end. The items are then joined with separator and wrapped in wrap_start and wrap_end. Return an empty array to print nothing. Does not affect the BreadcrumbList node in the schema graph.
| Parameter | Type | Description |
|---|---|---|
$links |
string[] |
The breadcrumb items as HTML. |
Returns string[] The items to print, as HTML.
add_filter('wpsseo_breadcrumb_links', function (array $links): array { $lastIndex = array_key_last($links); if ($lastIndex !== null) { $links[$lastIndex] = '<span aria-current="page">' . $links[$lastIndex] . '</span>'; } return $links; });
Robots.txt and scripts
wpsseo_robots_txt changes the robots.txt that WordPress generates. The script filters change the global Header Scripts and Body Scripts from the plugin settings. None of this output is cached.
wpsseo_robots_txt
filterFilters the robots.txt that Signocore SEO generates.
The plugin builds robots.txt from scratch on core's robots_txt filter at priority 11, so lines that other callbacks added before it are discarded. Add your rules here instead. The filter is skipped when the site is set to discourage search engines, because the file then disallows everything, and WordPress never handles the request when a physical robots.txt file exists in the web root.
| Parameter | Type | Description |
|---|---|---|
$output |
string |
The robots.txt content, ending with the Sitemap: lines. |
$companyName |
string |
The Company Name from the plugin settings, without HTML tags. |
Returns string The robots.txt content.
add_filter('wpsseo_robots_txt', function (string $output): string { $output .= "\n# AI training crawlers\n"; $output .= "User-agent: GPTBot\n"; $output .= "User-agent: CCBot\n"; $output .= "Disallow: /\n"; return $output; });
wpsseo_script_header
filterFilters the global Header Scripts before they are printed in wp_head.
Also runs when the Header Scripts setting is empty, so a callback can add scripts on its own. The result is printed as is, and an empty string prints nothing. Users who can manage_options get no global scripts unless Header Scripts For Admins is on, and the per-page scripts from the SEO box are printed separately, without this filter.
| Parameter | Type | Description |
|---|---|---|
$scripts |
string |
The Header Scripts setting, as HTML. |
Returns string The HTML to print, or an empty string for none.
add_filter('wpsseo_script_header', function (string $scripts): string { // Keep third-party tracking off the checkout page. if (function_exists('is_checkout') && is_checkout()) { return ''; } return $scripts; });
wpsseo_script_body
filterFilters the global Body Scripts before they are printed in wp_footer.
The Body Scripts print on wp_footer, near the closing </body> tag, not after the opening one. Also runs when the Body Scripts setting is empty, so a callback can add scripts on its own, and the result is printed as is. Users who can manage_options get no global scripts unless Body Scripts For Admins is on, and the per-page body scripts from the SEO box are printed separately, without this filter.
| Parameter | Type | Description |
|---|---|---|
$scripts |
string |
The Body Scripts setting, as HTML. |
Returns string The HTML to print, or an empty string for none.
add_filter('wpsseo_script_body', function (string $scripts): string { // The stored scripts use a {{PAGE_ID}} placeholder for the tracking pixel. return str_replace('{{PAGE_ID}}', (string) get_queried_object_id(), $scripts); });
WooCommerce
Signocore SEO keeps the functional WooCommerce pages, such as the cart, checkout and account pages, out of the sitemaps.
wpsseo_woocommerce_page_ids
filterFilters the WooCommerce page IDs that are left out of the sitemaps.
The list holds the cart, checkout, pay, order received, my account, edit address, view order, terms and conditions, and refund and returns pages, read from the WooCommerce page settings. Pages that are not set are skipped. The list is only used for the sitemaps, as part of wpsseo_sitemap_excluded_urls, and like that filter it only runs when a post type sitemap is built. With WPML or Polylang, the plugin translates the IDs to the current language in a callback at priority 10; use a later priority, such as 20, to work with the translated IDs.
| Parameter | Type | Description |
|---|---|---|
$pageIds |
int[] |
The page IDs. |
Returns int[] The page IDs to leave out of the sitemaps.
add_filter('wpsseo_woocommerce_page_ids', function (array $pageIds): array { // Keep the terms and conditions page in the sitemap. $termsPageId = (int) get_option('woocommerce_terms_page_id'); return array_values(array_diff($pageIds, [$termsPageId])); });
Deprecated hooks
These names were renamed. They still run, but log a deprecation notice when WP_DEBUG is on. Switch to the new name.
| Old name | Use instead | Deprecated in |
|---|---|---|
signocore_seo_schema_page_types |
wpsseo_schema_page_types |
6.0.0 |
signocore_seo_schema_graph |
wpsseo_schema_graph |
6.0.0 |
signocore_seo/sitemap_author_url |
wpsseo_sitemap_author_url |
6.0.0 |
signocoreseo_robots_txt |
wpsseo_robots_txt |
6.0.0 |