__( 'Critical', 'siteaudithub' ),
'high' => __( 'High', 'siteaudithub' ),
'medium' => __( 'Medium', 'siteaudithub' ),
'low' => __( 'Low', 'siteaudithub' ),
'info' => __( 'Informational', 'siteaudithub' ),
);
}
/**
* Classification labels.
*
* @return array
*/
public static function classifications() {
return array(
'confirmed' => __( 'Confirmed issue', 'siteaudithub' ),
'potential' => __( 'Potential issue', 'siteaudithub' ),
'manual' => __( 'Manual review required', 'siteaudithub' ),
'failed' => __( 'Scan failed', 'siteaudithub' ),
'inaccessible' => __( 'URL inaccessible', 'siteaudithub' ),
'unsupported' => __( 'Check unsupported', 'siteaudithub' ),
'unavailable' => __( 'Information unavailable', 'siteaudithub' ),
'not_evaluated' => __( 'Not evaluated', 'siteaudithub' ),
'integrity_mismatch' => __( 'Confirmed integrity mismatch', 'siteaudithub' ),
'signature_match' => __( 'Known signature match', 'siteaudithub' ),
'suspicious' => __( 'Suspicious code, needs review', 'siteaudithub' ),
'unverified' => __( 'Unverified file', 'siteaudithub' ),
);
}
/**
* Module labels.
*
* @return array
*/
public static function modules() {
return array(
'seo' => __( 'SEO', 'siteaudithub' ),
'a11y' => __( 'Accessibility', 'siteaudithub' ),
'health' => __( 'Technical Health', 'siteaudithub' ),
'plugins' => __( 'Plugins', 'siteaudithub' ),
'integrity' => __( 'Malware & Integrity', 'siteaudithub' ),
);
}
/**
* Label helper.
*
* @param string $type severity|classification|module.
* @param string $value Code.
* @return string
*/
public static function label( $type, $value ) {
$map = 'severity' === $type ? self::severities() : ( 'module' === $type ? self::modules() : self::classifications() );
return isset( $map[ $value ] ) ? $map[ $value ] : ucfirst( (string) $value );
}
/**
* All check definitions.
*
* Each: module, severity, class(ification), title, desc, fix, method.
*
* @return array
*/
public static function checks() {
if ( null !== self::$checks ) {
return self::$checks;
}
$c = array();
$add = static function ( $id, $module, $severity, $class, $title, $desc, $fix, $method ) use ( &$c ) {
$c[ $id ] = compact( 'module', 'severity', 'class', 'title', 'desc', 'fix', 'method' );
};
$html = __( 'Parsed the HTML returned to an anonymous visitor (no JavaScript executed).', 'siteaudithub' );
/* SEO ------------------------------------------------------------ */
$add( 'seo_http_error', 'seo', 'high', 'confirmed', __( 'Page returns an HTTP error', 'siteaudithub' ), __( 'The URL answered with a 4xx or 5xx status code, so visitors and search engines cannot reach the content.', 'siteaudithub' ), __( 'Restore the content, fix the server error, or redirect the URL to the most relevant live page and update internal links.', 'siteaudithub' ), __( 'HTTP status code of the response.', 'siteaudithub' ) );
$add( 'seo_redirect_loop', 'seo', 'high', 'confirmed', __( 'Redirect loop', 'siteaudithub' ), __( 'Following the redirects returns to a URL that was already visited, so the page never loads.', 'siteaudithub' ), __( 'Check redirect rules (redirect plugins, .htaccess, server config, HTTPS/www settings) and remove the circular rule.', 'siteaudithub' ), __( 'Redirects followed hop by hop.', 'siteaudithub' ) );
$add( 'seo_redirect_chain', 'seo', 'low', 'potential', __( 'Redirect chain', 'siteaudithub' ), __( 'The URL passes through two or more redirects before reaching the final page. Chains add latency and can dilute crawl efficiency.', 'siteaudithub' ), __( 'Point the first redirect (and internal links) straight at the final destination.', 'siteaudithub' ), __( 'Redirects followed hop by hop.', 'siteaudithub' ) );
$add( 'seo_title_missing', 'seo', 'high', 'confirmed', __( 'Missing page title', 'siteaudithub' ), __( 'The HTML has no
or it is empty. Titles are used in search results, browser tabs and by assistive technology.', 'siteaudithub' ), __( 'Set a descriptive, unique title in your theme or SEO plugin.', 'siteaudithub' ), $html );
$add( 'seo_title_multiple', 'seo', 'low', 'confirmed', __( 'Multiple elements', 'siteaudithub' ), __( 'More than one element was found. Browsers and search engines generally use only one, so the other is ignored.', 'siteaudithub' ), __( 'Make sure only one component (theme or SEO plugin) outputs the title tag.', 'siteaudithub' ), $html );
$add( 'seo_title_length', 'seo', 'low', 'potential', __( 'Title is very short or very long', 'siteaudithub' ), __( 'The title is under 15 or over 65 characters. Long titles are often truncated in search results; very short titles may not describe the page well. This is a guideline, not an error.', 'siteaudithub' ), __( 'Consider a concise, descriptive title that reflects the page content.', 'siteaudithub' ), $html );
$add( 'seo_title_duplicate', 'seo', 'medium', 'confirmed', __( 'Duplicate title', 'siteaudithub' ), __( 'Several scanned URLs share exactly the same title, which makes them harder to distinguish in search results.', 'siteaudithub' ), __( 'Give each page a unique title describing its specific content.', 'siteaudithub' ), __( 'Compared titles across all successfully scanned URLs.', 'siteaudithub' ) );
$add( 'seo_desc_missing', 'seo', 'low', 'confirmed', __( 'Missing meta description', 'siteaudithub' ), __( 'No meta description was found in the HTML. Search engines may then generate a snippet automatically.', 'siteaudithub' ), __( 'Write a short summary of the page in your SEO plugin or theme.', 'siteaudithub' ), $html );
$add( 'seo_desc_multiple', 'seo', 'low', 'confirmed', __( 'Multiple meta descriptions', 'siteaudithub' ), __( 'More than one meta description tag is output, usually because two plugins or a theme and a plugin both add one.', 'siteaudithub' ), __( 'Disable meta description output in all but one component.', 'siteaudithub' ), $html );
$add( 'seo_desc_duplicate', 'seo', 'low', 'confirmed', __( 'Duplicate meta description', 'siteaudithub' ), __( 'Several scanned URLs share exactly the same meta description.', 'siteaudithub' ), __( 'Write a distinct description for each important page.', 'siteaudithub' ), __( 'Compared descriptions across all successfully scanned URLs.', 'siteaudithub' ) );
$add( 'seo_canonical_missing', 'seo', 'low', 'potential', __( 'No canonical tag', 'siteaudithub' ), __( 'The page does not declare a canonical URL. Canonicals help search engines choose the preferred URL when duplicates exist.', 'siteaudithub' ), __( 'Most SEO plugins and WordPress core add a canonical for singular content; check your theme or SEO plugin configuration.', 'siteaudithub' ), $html );
$add( 'seo_canonical_multiple', 'seo', 'medium', 'confirmed', __( 'Multiple canonical tags', 'siteaudithub' ), __( 'More than one canonical link was found. Conflicting canonicals may be ignored by search engines.', 'siteaudithub' ), __( 'Keep a single canonical output (typically from your SEO plugin) and disable the others.', 'siteaudithub' ), $html );
$add( 'seo_canonical_other', 'seo', 'info', 'manual', __( 'Canonical points to a different URL', 'siteaudithub' ), __( 'The canonical tag names another URL as the preferred version. This is often intentional (pagination, parameters, syndicated content) but should be verified.', 'siteaudithub' ), __( 'Confirm the canonical target is the version you want indexed.', 'siteaudithub' ), $html );
$add( 'seo_canonical_broken', 'seo', 'medium', 'confirmed', __( 'Canonical target is broken or redirects', 'siteaudithub' ), __( 'The canonical URL itself returned an error, redirected, or is marked noindex in this scan.', 'siteaudithub' ), __( 'Point the canonical at a live, indexable URL that returns status 200.', 'siteaudithub' ), __( 'Matched canonical targets against scanned URL results.', 'siteaudithub' ) );
$add( 'seo_noindex', 'seo', 'info', 'manual', __( 'Page is marked noindex', 'siteaudithub' ), __( 'A robots meta tag or X-Robots-Tag header asks search engines not to index this URL. This is correct for private or utility pages but harmful if unintended.', 'siteaudithub' ), __( 'Verify this page should be excluded from search results; otherwise remove the noindex directive in your SEO plugin.', 'siteaudithub' ), __( 'Robots meta tags and X-Robots-Tag response header.', 'siteaudithub' ) );
$add( 'seo_noindex_in_sitemap', 'seo', 'medium', 'potential', __( 'Noindex page listed in sitemap', 'siteaudithub' ), __( 'The URL is in the XML sitemap but also marked noindex, which sends mixed signals to search engines.', 'siteaudithub' ), __( 'Either remove the noindex directive or exclude the URL from the sitemap.', 'siteaudithub' ), __( 'Compared sitemap entries with robots directives.', 'siteaudithub' ) );
$add( 'seo_robots_blocked', 'seo', 'medium', 'potential', __( 'URL disallowed by robots.txt', 'siteaudithub' ), __( 'A Disallow rule for all user agents matches this URL, although it is published content or listed in the sitemap.', 'siteaudithub' ), __( 'If the page should be crawled, adjust robots.txt. If not, also remove it from the sitemap.', 'siteaudithub' ), __( 'Simplified robots.txt matching for "User-agent: *" groups (Allow/Disallow, * and $ wildcards).', 'siteaudithub' ) );
$add( 'seo_search_discouraged', 'seo', 'high', 'confirmed', __( 'Search engine visibility is discouraged', 'siteaudithub' ), __( 'Settings → Reading → "Discourage search engines from indexing this site" is enabled.', 'siteaudithub' ), __( 'Disable the option when the site is ready for public search traffic (keep it on for staging sites).', 'siteaudithub' ), __( 'WordPress blog_public option.', 'siteaudithub' ) );
$add( 'seo_h1_missing', 'seo', 'medium', 'potential', __( 'No H1 heading', 'siteaudithub' ), __( 'The page has no H1. A clear main heading helps visitors, assistive technology and search engines understand the topic.', 'siteaudithub' ), __( 'Add one main heading that describes the page (often the post or page title).', 'siteaudithub' ), $html );
$add( 'seo_h1_multiple', 'seo', 'info', 'manual', __( 'Multiple H1 headings', 'siteaudithub' ), __( 'More than one H1 was found. This is valid HTML and is not by itself a ranking penalty, but a single clear main heading usually makes structure easier to follow.', 'siteaudithub' ), __( 'Review whether the extra H1s (often logos or widgets) should be lower-level headings.', 'siteaudithub' ), $html );
$add( 'seo_img_alt', 'seo', 'low', 'confirmed', __( 'Images without alt attributes', 'siteaudithub' ), __( 'Some images have no alt attribute, so search engines get less context about them. See the Accessibility module for the user impact.', 'siteaudithub' ), __( 'Add descriptive alt text to meaningful images and alt="" to decorative ones.', 'siteaudithub' ), $html );
$add( 'seo_broken_internal_link', 'seo', 'high', 'confirmed', __( 'Broken internal link', 'siteaudithub' ), __( 'This page links to an internal URL that returned an error or could not be loaded during this scan.', 'siteaudithub' ), __( 'Update or remove the link, or restore/redirect the target.', 'siteaudithub' ), __( 'Matched outbound links against scanned URL results.', 'siteaudithub' ) );
$add( 'seo_internal_link_redirect', 'seo', 'low', 'potential', __( 'Internal link points to a redirect', 'siteaudithub' ), __( 'The link target redirects elsewhere. Linking directly to the final URL avoids an extra request.', 'siteaudithub' ), __( 'Update the link to the final destination URL.', 'siteaudithub' ), __( 'Matched outbound links against scanned URL results.', 'siteaudithub' ) );
$add( 'seo_broken_external_link', 'seo', 'medium', 'confirmed', __( 'Broken external link', 'siteaudithub' ), __( 'An external link returned 404/410 or a server error during this scan.', 'siteaudithub' ), __( 'Update the link to a working resource or remove it.', 'siteaudithub' ), __( 'HEAD (falling back to GET) request to each unique external URL, when external checking is enabled.', 'siteaudithub' ) );
$add( 'seo_external_link_blocked', 'seo', 'info', 'manual', __( 'External link could not be verified', 'siteaudithub' ), __( 'The external site refused automated requests (for example 401, 403 or 429) or timed out. The link may work fine for real visitors.', 'siteaudithub' ), __( 'Open the link in a browser to confirm it works.', 'siteaudithub' ), __( 'HEAD/GET request to the external URL.', 'siteaudithub' ) );
$add( 'seo_sitemap_missing', 'seo', 'medium', 'potential', __( 'No XML sitemap found', 'siteaudithub' ), __( 'Neither /wp-sitemap.xml, /sitemap_index.xml nor /sitemap.xml returned a parseable sitemap.', 'siteaudithub' ), __( 'Enable the WordPress core sitemap or your SEO plugin sitemap and reference it in robots.txt.', 'siteaudithub' ), __( 'Requested common sitemap locations.', 'siteaudithub' ) );
$add( 'seo_sitemap_coverage', 'seo', 'low', 'potential', __( 'Published content missing from the sitemap', 'siteaudithub' ), __( 'Some published URLs discovered through WordPress were not found in the sitemap.', 'siteaudithub' ), __( 'Check sitemap settings in your SEO plugin (excluded post types, noindex settings).', 'siteaudithub' ), __( 'Compared WordPress-discovered URLs with sitemap entries (within the scan limits).', 'siteaudithub' ) );
$add( 'seo_thin_content', 'seo', 'low', 'potential', __( 'Low word count', 'siteaudithub' ), __( 'The page has fewer visible words than the configured threshold. Short pages are not necessarily a problem (contact pages, galleries), so treat this as a signal to review.', 'siteaudithub' ), __( 'Consider whether the page fully answers its purpose; expand, merge or noindex it if appropriate.', 'siteaudithub' ), __( 'Counted words in visible body text, excluding scripts and styles.', 'siteaudithub' ) );
$add( 'seo_duplicate_content', 'seo', 'medium', 'potential', __( 'Possible duplicate content', 'siteaudithub' ), __( 'The visible text of this page is identical to another scanned URL.', 'siteaudithub' ), __( 'Consolidate duplicates, use canonical tags, or differentiate the content.', 'siteaudithub' ), __( 'Exact hash of normalized visible text (pages with 50+ words).', 'siteaudithub' ) );
$add( 'seo_orphan_page', 'seo', 'low', 'potential', __( 'No internal links found to this page', 'siteaudithub' ), __( 'None of the scanned pages link to this URL. Pages without internal links are harder for visitors and crawlers to find. Links from pages outside the scan scope are not counted.', 'siteaudithub' ), __( 'Link to the page from related content or navigation.', 'siteaudithub' ), __( 'Inbound link count across scanned pages.', 'siteaudithub' ) );
$add( 'seo_mixed_content', 'seo', 'medium', 'confirmed', __( 'Mixed content on HTTPS page', 'siteaudithub' ), __( 'The HTTPS page loads images, scripts, frames or stylesheets over insecure http://. Browsers may block them or show warnings.', 'siteaudithub' ), __( 'Update the resource URLs to https:// (a search-replace on the database is often needed after an HTTPS migration).', 'siteaudithub' ), $html );
$add( 'seo_jsonld_invalid', 'seo', 'medium', 'confirmed', __( 'Invalid structured data (JSON-LD)', 'siteaudithub' ), __( 'A JSON-LD script block could not be parsed as JSON, so it will be ignored.', 'siteaudithub' ), __( 'Fix the plugin or template generating the structured data, then validate it with a structured data testing tool.', 'siteaudithub' ), __( 'JSON parse of each application/ld+json block.', 'siteaudithub' ) );
$add( 'seo_meta_refresh', 'seo', 'low', 'potential', __( 'Meta refresh redirect', 'siteaudithub' ), __( 'The page uses a meta refresh. Server-side redirects are clearer for visitors and search engines.', 'siteaudithub' ), __( 'Replace with a 301/302 server redirect where appropriate.', 'siteaudithub' ), $html );
$add( 'seo_js_rendered', 'seo', 'info', 'manual', __( 'Content may be rendered by JavaScript', 'siteaudithub' ), __( 'Very little text was found in the HTML while the page loads many scripts. SiteAuditHub does not execute JavaScript, so content added by scripts was not evaluated.', 'siteaudithub' ), __( 'Review this page manually in a browser and with a rendering-capable tool.', 'siteaudithub' ), $html );
/* Accessibility --------------------------------------------------- */
$add( 'a11y_img_alt', 'a11y', 'medium', 'confirmed', __( 'Images missing alternative text', 'siteaudithub' ), __( 'Images without an alt attribute are announced by file name or skipped by screen readers.', 'siteaudithub' ), __( 'Add alt text describing the purpose of each meaningful image; use alt="" for purely decorative images.', 'siteaudithub' ), $html );
$add( 'a11y_button_name', 'a11y', 'high', 'confirmed', __( 'Buttons without an accessible name', 'siteaudithub' ), __( 'Buttons with no text, aria-label or title are announced only as "button".', 'siteaudithub' ), __( 'Add visible text or an aria-label describing the action (e.g. "Close menu").', 'siteaudithub' ), $html );
$add( 'a11y_link_name', 'a11y', 'high', 'confirmed', __( 'Links without an accessible name', 'siteaudithub' ), __( 'Links with no text (often icon-only links or images without alt) cannot be understood by screen reader users.', 'siteaudithub' ), __( 'Add link text, an aria-label, or alt text on the linked image.', 'siteaudithub' ), $html );
$add( 'a11y_link_generic', 'a11y', 'low', 'potential', __( 'Non-descriptive link text', 'siteaudithub' ), __( 'Links such as "click here" or "read more" do not make sense out of context, for example in a screen reader links list.', 'siteaudithub' ), __( 'Use link text that describes the destination, or add visually hidden context.', 'siteaudithub' ), $html );
$add( 'a11y_lang_missing', 'a11y', 'medium', 'confirmed', __( 'Missing document language', 'siteaudithub' ), __( 'The element has no lang attribute, so screen readers may use the wrong pronunciation.', 'siteaudithub' ), __( 'Ensure your theme outputs language_attributes() on the tag.', 'siteaudithub' ), $html );
$add( 'a11y_title_missing', 'a11y', 'medium', 'confirmed', __( 'Missing document title', 'siteaudithub' ), __( 'The page has no title, which is the first thing screen readers announce and what identifies the browser tab.', 'siteaudithub' ), __( 'Make sure the theme supports title-tag and the page has a title.', 'siteaudithub' ), $html );
$add( 'a11y_heading_skip', 'a11y', 'low', 'potential', __( 'Skipped heading levels', 'siteaudithub' ), __( 'Heading levels jump (for example H2 to H4). Screen reader users navigate by headings, and gaps can suggest missing content.', 'siteaudithub' ), __( 'Use heading levels in order to reflect the content outline; style them with CSS instead of picking levels for size.', 'siteaudithub' ), $html );
$add( 'a11y_heading_empty', 'a11y', 'low', 'confirmed', __( 'Empty headings', 'siteaudithub' ), __( 'Headings without text are announced as empty headings.', 'siteaudithub' ), __( 'Remove empty heading elements or add text.', 'siteaudithub' ), $html );
$add( 'a11y_aria_role', 'a11y', 'medium', 'confirmed', __( 'Invalid ARIA role', 'siteaudithub' ), __( 'These role values are not defined in WAI-ARIA, so assistive technology ignores them.', 'siteaudithub' ), __( 'Use a valid role or rely on native HTML elements.', 'siteaudithub' ), $html );
$add( 'a11y_aria_attr', 'a11y', 'low', 'potential', __( 'Unknown ARIA attribute', 'siteaudithub' ), __( 'These aria-* attributes are not part of WAI-ARIA 1.2 (often typos).', 'siteaudithub' ), __( 'Correct the attribute name or remove it.', 'siteaudithub' ), $html );
$add( 'a11y_aria_hidden_focus', 'a11y', 'high', 'confirmed', __( 'Focusable element hidden from assistive technology', 'siteaudithub' ), __( 'aria-hidden="true" is set on an element that can receive keyboard focus, so keyboard users reach something screen readers cannot describe.', 'siteaudithub' ), __( 'Remove aria-hidden, or make the element unfocusable (tabindex="-1" / disabled) when it is truly hidden.', 'siteaudithub' ), $html );
$add( 'a11y_aria_ref', 'a11y', 'medium', 'confirmed', __( 'ARIA reference to a missing element', 'siteaudithub' ), __( 'aria-labelledby or aria-describedby points to an id that does not exist on the page.', 'siteaudithub' ), __( 'Fix the referenced id or add the missing element.', 'siteaudithub' ), $html );
$add( 'a11y_duplicate_id', 'a11y', 'low', 'potential', __( 'Duplicate id attributes', 'siteaudithub' ), __( 'The same id is used more than once. This can break label and ARIA associations.', 'siteaudithub' ), __( 'Make ids unique on each page.', 'siteaudithub' ), $html );
$add( 'a11y_tabindex', 'a11y', 'low', 'potential', __( 'Positive tabindex values', 'siteaudithub' ), __( 'tabindex values above 0 override the natural focus order and often create a confusing keyboard experience.', 'siteaudithub' ), __( 'Use tabindex="0" or "-1" and arrange the DOM in a logical order.', 'siteaudithub' ), $html );
$add( 'a11y_click_handler', 'a11y', 'medium', 'potential', __( 'Clickable elements that may not be keyboard accessible', 'siteaudithub' ), __( 'Non-interactive elements (div, span…) have onclick handlers but no role or tabindex, so keyboard users may not be able to activate them.', 'siteaudithub' ), __( 'Use