Show post and page IDs in the WordPress admin by enabling Custom Admin Columns in WP Adminify: go to WP Adminify > Productivity, turn on Custom Admin Columns, tick Show Post/Page ID Column, and save, and an ID column appears on the Posts and Pages list screens. WordPress core has never printed the ID in that list even though every row already carries it, because the list table renders each row as <tr id="post-123"> and every Edit link in the row already points at post.php?post=123, so the ID is loaded and printed into the page HTML while the only built-in way to read it is hovering a link and checking the browser status bar one post at a time.
That is the whole answer. The rest of this post is why the hover trick is evidence, why your IDs look random, the code route, and the migration warning nobody puts in these tutorials.
The workaround is the proof
Every guide on this topic teaches the same thing. Hover Edit, read post.php?post=123 off the status bar. Or open the post and read the ID out of the address bar.
Both work. Both also quietly admit that the ID is already rendered. Look at what WP_Posts_List_Table actually emits for one row:
<tr id="post-123"
class="iedit author-self level-0 post-123 type-page status-publish hentry">
<td class="title column-title">
<a href="/wp-admin/post.php?post=123&action=edit">Sample Page</a>
<div class="row-actions">
<span class="edit"><a href="/wp-admin/post.php?post=123&action=edit">Edit</a></span>
<span class="trash"><a href="/wp-admin/post.php?post=123&action=trash">Trash</a></span>
<span class="view"><a href="/?page_id=123">View</a></span>
</div>
</td>
...
</tr>
Enter fullscreen mode Exit fullscreen mode
The ID is the row’s HTML id. It repeats in the row classes, courtesy of get_post_class(). It appears in four URLs. On a 20-row screen that is roughly a hundred printed copies of numbers you cannot read.
This is worth separating from the other famous admin blind spot. A missing featured image column is a genuine absence, the featured image is postmeta and the list table does not render postmeta. The post ID is the opposite, it is the primary key of the row the list table is already looping over. It is present and unprinted, because core treats the ID as a database internal rather than something a publisher needs.
Why your IDs look random
The most common follow-up question on this topic: why is my third page ID 6?
Because wp_posts has one auto_increment primary key, and everything lives in that table. Posts, pages, custom post types, attachments, revisions, autosaves, menu items, all of it draws from the same counter.
ID 1 -> "Hello world!" post
ID 2 -> "Sample Page"
ID 3 -> "Privacy Policy" (auto-draft)
ID 4 -> autosave / revision
ID 5 -> an uploaded image (attachment)
ID 6 -> "Home"
Enter fullscreen mode Exit fullscreen mode
Which is exactly what the demo shows: Home 6, Privacy Policy 3, Sample Page 2. There is no per-post-type sequence, gaps are normal, and IDs are never guessable. Anybody who has tried to work out an ID by counting rows has learned this the slow way.
What it actually costs
One ID is a five second hover. The pain is bulk.
- An Elementor or query loop excluding twelve specific pages
- A gallery or posts shortcode taking a comma-separated ID list
- A
post__in/post__not_inargument inWP_Query - A
wp post get 123run against a client site with WP-CLI - Two pages both titled “Home”, where the ID is the only unambiguous handle
Twelve IDs means twelve hovers and twelve hand transcriptions. And IDs are unforgiving in a specific way: a wrong digit does not error. post__not_in => [124] excludes some other post entirely and the loop renders happily. You find it when a client asks why the wrong page is showing.
The code route, and its fine print
// functions.php: ID column on posts and pages
add_filter('manage_posts_columns', 'wpa_id_column');
add_filter('manage_pages_columns', 'wpa_id_column');
function wpa_id_column($columns) {
// insert ID right after the checkbox column
return array_slice($columns, 0, 1, true)
+ ['wpa_id' => 'ID']
+ array_slice($columns, 1, null, true);
}
add_action('manage_posts_custom_column', 'wpa_id_value', 10, 2);
add_action('manage_pages_custom_column', 'wpa_id_value', 10, 2);
function wpa_id_value($column, $post_id) {
if ($column === 'wpa_id') {
echo (int) $post_id;
}
}
Enter fullscreen mode Exit fullscreen mode
The hook reference is manage_{$post_type}_posts_columns. The fine print:
- Posts and pages take separate filter pairs, and every custom post type wants its own if you need per-type control
- Sorting is not included, that is a third hook (
manage_edit-post_sortable_columns) plus apre_get_poststweak - The column claims whatever width it likes until you add admin CSS
- It lives in
functions.php, so it dies on theme switch unless you promote it to a must-use plugin
The checkbox route
I did this run with WP Adminify’s Productivity module. Enable Custom Admin Columns, tick Show Post/Page ID Column, save.
- An ID column appears on the Posts and Pages lists, no code, survives theme switches
- The same panel carries the rest of the list-screen columns people usually snippet in one at a time, Comment ID with parent ID, Taxonomy ID, a URL Path column with per-post-type selection, and a Last Login column for users, all on the Custom Admin Columns docs page
- If you outgrow fixed checkboxes, the standalone Admin Columns Editor builds columns for arbitrary fields, and how to add an ACF field to an admin column is the worked example for meta fields
- This rundown of necessary admin columns for WordPress is a decent map of which columns earn their width, and column customization for any post type covers the CPT side
- It sits inside the wider Productivity toolset
The caveats I will not skip. An ID column is display, not magic, it does not validate anything and it does not make an ID portable. It costs horizontal space on a screen that is already tight, Screen Options is where you claw that back. The docs describe this checkbox as being for “post and page table lists”, so I am not claiming custom post type coverage until someone verifies it on a real CPT. And the column is not documented as sortable, so treat sorting by ID as unconfirmed.
The warning that matters more than the column
A post ID is unique to one database, not to your site.
Export the content and re-import it, rebuild staging from a fresh install, migrate hosts with a content-level tool, and the same page comes back with a different ID. Every template, shortcode, exclude list and option row referencing the old number keeps running. Nothing throws. The wrong content renders.
So read IDs, use them, and prefer slugs wherever the API accepts one. Where you do hardcode, put the ID in a config constant or an option rather than scattered through templates, so the fix after a migration is one line and not a grep.
Docs: Show Post/Page ID Column in WP Adminify
So: ID column always on, snippet it per post type, or still hovering the Edit link? And be honest, how many hardcoded IDs are in your production templates right now?
답글 남기기