Add post id to url instead of WordPress default -2 suffix

First, go to wherever you defined the custom post type. For instance, if I were writing a function to define a custom post type “books”, I would use register_post_type( 'books', $args ) where $args is an array used to configure the post type properties. By default, custom post types use the post type key as their slug. In order to define a custom slug, we need to use the rewrite key of that $args array. In the books example, and using the URL scheme you specified in your question, it might look like this:

  $args = array();
  $args['rewrite'] = array(
    'slug' => 'books/%post_id%'

You are likely specifying other parameters than just rewrite in your $args array. If you happen to need more background info about them see register_post_type.

If we stopped here, that %post_id% rewrite tag would show up unaltered in the URLs of all posts of this post type. We want to replace it with the post’s actual id. To do this, we hook into the post_type_link filter.

function wpse415642_rewrite_links( $post_link, $post_obj ){
  if ( 'books' == $post_obj->post_type ){
    $post_link = str_replace('%post_id%', $post_obj->ID, $post_link);
  return $post_link;

add_filter( 'post_type_link', 'wpse415642_rewrite_links', 10, 2 );

For some custom url slugs you would have to go further here and register the slug as a query var so WordPress could correctly match the URL path. Luckily %post_id% is a registered rewrite tag by default so we don’t have to do that. However, you should be sure to flush your permalinks in permalink settings so the new url scheme takes effect.

The part of WordPress in charge of generating unique post slugs doesn’t know about our new URLs so we have to manually tell it not to add the “-2” style suffixes. Otherwise, to stay with the books example, we will have:

  • books/1234/count-of-monte-cristo
  • books/2345/count-of-monte-cristo-2

as slugs for two posts both called ‘Count of Monte Cristo’. In order to prevent the unique slug generation we hook into the pre_wp_unique_post_slug filter and return a non-null value.

function wpse415642_prevent_unique_slug( $generate_unique_slug, $slug, $id, $post_status, $post_type ){
  if ( 'books' == $post_type ){
    return $slug;
  return $generate_unique_slug;

add_filter('pre_wp_unique_post_slug', 'wpse415642_prevent_unique_slug', 10, 5 );

This will give you unique urls without any suffixes on the post title.