I have thousands of posts on my site which are created automatically. Some of them have duplicated titles. I'd like to add some keywords to their slugs, so URLs like




I can easily do that using wp_update_post function and normally a URL domain.com/post-title-2/ will automatically redirect to domain.com/post-title-2-some-other-keywords/. However, if I add a new post with the same title, it's URL will be domain.com/post-title-2/ (since old one doesn't exist anymore) and that redirection won't work anymore.

How to avoid this?

I want a new post to have the URL domain.com/post-title-3/, even though domain.com/post-title-2/ is available. Is there any other option, besides saving old slugs in a separate table?

Update: I'm aware that old slugs are kept in wp_postmeta, but that doesn't help with the redirection problem.

  • Forgot to say that I'm aware that old slugs are kept in wp_postmeta but that doesn't help with the redirection problem. – Sibul Apr 12 '15 at 10:32

This'll probably have a few quirks, but in practice it'll do what you're after. Note that it's set to only work for posts (not pages or custom post types).

All we're doing is using the wp_unique_post_slug filter, which runs after WordPress has generated it's own unique slug, and additionally checking it against all _wp_old_slug meta values. You'll see in my custom query I still check it against slugs in the main posts table - without it, we could potentially "reset" the suffix count and create a collision with an existing post.

function wpse_183975_unique_post_slug( $slug, $post_id, $post_status, $post_type ) {
    if ( $post_status === 'publish' && $post_type === 'post' ) {
        global $wpdb;

        if ( preg_match( '/-([0-9]+)$/', $slug, $match ) ) { // Already has a number suffix
            $slug_base = substr( $slug, 0, -strlen( $match[0] ) ); // Strip suffix from slug
            $i = ( int ) $match[1];
        } else { // No suffix, start at 1
            $slug_base = $slug;
            $i = 1;

        while ( // For as long as there is an existing slug, increment the suffix
                    "SELECT m.post_id FROM $wpdb->postmeta AS m INNER JOIN $wpdb->posts AS p ON m.post_id = p.ID " .
                    "WHERE " .
                        "p.ID != %d AND " .
                        "p.post_type = 'post' AND " .
                        "( p.post_status = 'publish' AND p.post_name = %s ) OR " .
                        "( m.meta_key = '_wp_old_slug' AND m.meta_value = %s ) " .
                    "LIMIT 1",
        ) {
            $slug = "$slug_base-" . ++$i;

    return $slug;

add_filter( 'wp_unique_post_slug', 'wpse_183975_unique_post_slug', 10, 4 );
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.