add_query_vars and canonical links

WordPress thinks you’re on those pages because of the pagename query var in your rewrite rules.

Your journey is going to start in wp-includes/default-filters.php, which every WP developer should known and love: it’s where the WordPress care uses its own plugin API to hook in and change stuff.

Of interest to your question:

<?php
add_action( 'wp_head',             'rel_canonical'                          );

If you take a look at the rel canonical function in wp-includes/link-template.php you find this:

<?php
/**
 * Output rel=canonical for singular queries.
 *
 * @package WordPress
 * @since 2.9.0
*/
function rel_canonical() {
    if ( !is_singular() )
        return;

    global $wp_the_query;
    if ( !$id = $wp_the_query->get_queried_object_id() )
        return;

    $link = get_permalink( $id );

    if ( $page = get_query_var('cpage') )
        $link = get_comments_pagenum_link( $page );

    echo "<link rel="canonical" href="https://wordpress.stackexchange.com/questions/64535/$link" />\n";
}

Unfortunately no filters to modify the link. So what you’ll need to do is hook in someplace late, but before wp_head fires, check for your query vars, unhook the rel_canonical function and roll your own.

First step, clean up your rewrites a bit:

<?php
add_action('init', 'wpse64535_add_rewrite_rules');
function wpse64535_add_rewrite_rules()
{
    add_rewrite_rule(
        '^roller-derbies/([^/]+)/?$',
        'index.php?pagename=states&var_state=$matches[1]',
        'top'
    );

    add_rewrite_rule(
        '^roller-derbies/([^/]+)/([^/]+)/?$',
        'index.php?pagename=cities&var_state=$matches[1]&var_city=$matches[2]',
        'top'
    );
}

add_filter('query_vars', 'wpse64535_add_query_vars');
function wpse64535_add_query_vars($vars)
{
    $vars[] = 'var_state';
    $vars[] = 'var_city';
    return $vars;
}

Second part, hook into template_redirect and check for the custom query variables. If you find either, remove the default rel_canonical function and use your own.

<?php
add_action('template_redirect', 'wpse64535_maybe_fix');
function wpse64535_maybe_fix()
{
    if(get_query_var('var_state') || get_query_var('var_city'))
    {
        remove_action('wp_head', 'rel_canonical');
        add_action('wp_head', 'wpse64535_fix_canonical');
    }
}

function wpse64535_fix_canonical()
{
    $link = home_url('roller-derbies/');

    // might want to validate these before just using them?
    if($state = get_query_var('var_state'))
        $link .= "{$state}/";

    if($city = get_query_var('var_city'))
        $link .= "{$city}/";

    echo '<link rel="canonical" href="' . esc_url($link) . '" />';
}

All of the above as a plugin.