30. Content Scorer | SEO Forge - Rank Higher with AI-Powered SEO
Download Log in

30. Content Scorer

Developer Guide

The content scorer powers the “SEO” column in the posts list table, the metabox score indicator, and the analysis API.

Score Display Colors

Score RangeColorHex
80-100 (Good)Green#059669
50-79 (Medium)Yellow#D97706
0-49 (Bad)Red#DC2626

Rule-Based Scoring Algorithm (13+ Checks)

Score starts at 100 and deductions are applied:

CheckConditionSeverityDeduction
Title too short< 20 characterserror-10
Title too long> 70 characterswarning-5
Meta description missingEmpty or not seterror-15
Meta description too short< 120 characterswarning-5
Meta description too long> 170 characterswarning-3
Content too short< 100 wordserror-15
Content could be longer< 300 wordswarning-5
No focus keyword setEmpty or not setwarning-10
Focus keyword not in titleKeyword absent from SEO titlewarning-8
Focus keyword not in descriptionKeyword absent from meta descinfo-3
Focus keyword not in contentKeyword absent from post bodyerror-10
Keyword density too high> 3% of total wordswarning-5
Keyword not in first paragraphAbsent from first 200 charsinfo-3
No headings in contentNo H2-H6 tagswarning-5
Images missing alt text without altwarning-5
No imagesZero tagsinfo-2
No internal linksZero same-domain tagswarning-5
Sentences too longAvg > 25 wordswarning-5
Flesch score very low< 30warning-5
Excessive passive voice> 20% passiveinfo-3

Custom Scoring Rules

You can modify the effective score or add custom checks by hooking into the analysis flow:

php
// Add a custom scoring rule that penalizes posts without a call-to-action
add_action( 'save_post', function ( $post_id, $post ) {
    if ( wp_is_post_revision( $post_id ) || $post->post_status !== 'publish' ) {
        return;
    }

    $content = $post->post_content;
    $has_cta = preg_match( '/<a[^>]*class="[^"]*cta[^"]*"/i', $content )
            || stripos( $content, 'sign up' ) !== false
            || stripos( $content, 'get started' ) !== false;

    // Store a flag for your custom reports
    update_post_meta( $post_id, '_custom_has_cta', $has_cta ? '1' : '' );
}, 25, 2 );
php
// Build a custom scoring overlay that extends SEO Forge scores
function my_custom_seo_score( $post_id ) {
    $cached = SEOFORGE_Analyzer::instance()->get_cached( $post_id );
    $base_score = $cached ? (int) $cached->score : 0;

    $penalties = 0;

    // Custom check: post must have at least one external link
    $content = get_post_field( 'post_content', $post_id );
    $site_url = home_url();
    preg_match_all( '/<a[^>]+href="([^"]+)"/i', $content, $matches );
    $has_external = false;
    foreach ( $matches[1] as $url ) {
        if ( strpos( $url, $site_url ) === false && strpos( $url, 'http' ) === 0 ) {
            $has_external = true;
            break;
        }
    }
    if ( ! $has_external ) {
        $penalties += 5;
    }

    return max( 0, $base_score - $penalties );
}

Forge AI Assistant Online

Hi! I'm the SEO Forge AI assistant. Ask me anything about the plugin — setup, features, troubleshooting, or development.

Just now
Powered by Forge AI · Browse docs