I found two possible solutions to my problem :
1. the link to the post should not use permalink
the problem is actually that a draft post has a permalink with its post_id instead of post_name, as explained here : How to make draft posts or posts in review accessible via full url / slug?
so, in my case i could find where the link is generated, and it was getting the link with get_permalink()
which will return the url with post_id if the post is a draft. So instead i now use home_url($post->post_name)
to redirect to the post with an url with post_name : https://my_website.com/my_page
2. redirect the page to it’s version without post_id
additionally, on the post i use the template_redirect
hook to check if the current uri match the post_name (after a bunch of tests to check if I’m on the right post), if not I redirect the page to the url with post_name :
function check_url_for_post_id() {
if (!is_my_post()) {
return;
}
$post_id = get_the_ID();
$current_post = get_post($post_id);
if (is_null($current_post)) {
return;
}
$current_slug = trim($_SERVER['REQUEST_URI'], "https://wordpress.stackexchange.com/");
$slug = trim($current_post->post_name, "https://wordpress.stackexchange.com/");
if ($slug !== $current_slug) {
wp_safe_redirect(home_url($slug));
exit;
}
}
add_action('template_redirect', 'check_url_for_post_id');
So i’m using $_SERVER
and I don’t know if it’s safe ? on this question, some people said we should not use $_GET and $_POST, and I suppose also not $_REQUEST or $_SERVER because they are vulnerable to attacks : https://stackoverflow.com/questions/13652605/extracting-a-parameter-from-a-url-in-wordpress
anyway, I suppose that in my case it’s ok, since I only use it for comparison, I don’t use it’s content otherwise