Skip to content

How WordPress Revisions, Autosaves and Auto-Drafts Work — and How to Manage the History in Your Database

When you edit a post in WordPress and click “Update”, a “Revisions” item appears in the sidebar, letting you compare earlier versions and restore one. It is a useful safety net against accidental overwrites. At the same time, maintenance advice often warns that revisions pile up in the database and should be capped.

Where exactly are revisions stored, and in what form? How do they differ from autosaves and auto-drafts? As promised at the end of the previous article on autoload options, this post answers those questions using read-only queries against this blog’s own WordPress database (7.1 series) and the core source code.

Revisions live in the same table as your posts

Revisions have no table of their own. They are stored in wp_posts, as rows whose post_type is revision. Grouping this blog’s wp_posts by type and status (with SELECT only; nothing was changed):

post_type post_status Rows Total content bytes
post publish 126 1,144,680
revision inherit 42 396,549
page draft 1 6,788

A revision always has post_status = inherit, its parent’s ID in post_parent, and a post_name like 7-revision-v1. Note the byte count: 42 revisions hold about 390 KB of content. A revision is a full copy of the content, not a diff. The single post with four revisions (ID 7) accounts for about 64 KB by itself.

When revisions are created

In wp-includes/default-filters.php, revision creation is attached to post-saving hooks (see WordPress action and filter hooks explained):

add_action( 'wp_after_insert_post', 'wp_save_post_revision_on_insert', 9, 3 );
add_action( 'post_updated', 'wp_save_post_revision', 10, 1 );

wp_save_post_revision_on_insert() returns immediately when $update is false, so a revision is created only when an existing post is updated. wp_save_post_revision() also does nothing during an autosave, for post types without revision support, for auto-draft posts, when revisions are disabled, or when the title, content and excerpt are unchanged from the latest revision (compared after whitespace normalization). Clicking “Update” without changing anything adds no revision.

Measured: only updated posts have revisions

This blog publishes with wp post create from WP-CLI rather than the editor. Creation is not an update, so no revision is made at publish time. Revisions appear only when a post is later changed with wp post update, for example to add internal links. The numbers match exactly:

Measurement Count
Posts 126
Posts whose post_modified differs from post_date 31
Posts with at least one revision 31
Total revisions 42

The 95 posts that were never updated have no revisions at all.

A revision records the state after saving

Comparing each post’s revisions with its current content, the newest revision matched the current content for all 31 posts, while the oldest revision of every post with two or more revisions did not. As a core comment puts it, the most recent revision always matches the current post. A revision is a snapshot of the version after the save, not before it.

That leads to a less obvious consequence. For the 23 posts with exactly one revision, that revision equals the current content. So where is the original, pre-update version? Nowhere. With the editor, opening “Add New” creates a placeholder row and clicking “Publish” counts as an update, so the published version is kept. A post created directly in the published state (via WP-CLI, an import or an external tool) has no revision of its first version, and after one update only the updated version remains.

This is by design, not a bug, but it means revisions do not guarantee you can return to the original, and they are not a substitute for backups (see the 3-2-1 backup rule).

Autosaves and auto-drafts

While you write, WordPress autosaves every AUTOSAVE_INTERVAL seconds (default 60, also 60 here). For a draft being edited by its author, the autosave overwrites the post row itself. For a published post, it goes into a separate row named like 7-autosave-v1, so unsaved edits never leak onto the live page. There is one autosave row per user, overwritten each time, and because wp_save_post_revision() skips autosaves, autosaving does not add regular revisions. This blog has zero autosave rows; all 42 are -revision-v1.

An auto-draft is the placeholder row created as soon as someone opens “Add New”, so media can be attached before the first save. A daily WP-Cron event, wp_scheduled_auto_draft_delete, runs wp_delete_auto_drafts(), which force-deletes auto-drafts older than seven days. If WP-Cron is not running, that cleanup does not happen either (see crontab syntax basics vs WP-Cron). Similarly, wp_scheduled_delete empties trashed posts after EMPTY_TRASH_DAYS (default 30).

Type What it is Growth Automatic cleanup
Revision Full snapshot per update One per changed update Only if a limit is set
Autosave In-progress edits One per user, overwritten Not needed
Auto-draft Placeholder for “Add New” One per screen opened WP-Cron after 7 days
Trash Deleted posts One per deletion WP-Cron after 30 days

With default settings, revisions are the only one that grows without limit.

Limiting revisions with WP_POST_REVISIONS

wp_revisions_to_keep() reads the WP_POST_REVISIONS constant (see key wp-config.php constants): true (the default) means unlimited and becomes -1, a positive integer keeps that many per post, and false or 0 disables revisions. This blog leaves it undefined, so it returns -1. The wp_revisions_to_keep and wp_{post_type}_revisions_to_keep filters can override the constant, so a plugin may make your setting appear to have no effect.

The important detail is when the limit is enforced. In wp_save_post_revision(), old revisions are trimmed only right after a new revision is saved, and only for that post. Setting the limit to 5 does not touch a post that already has 20 revisions until that post is updated again. A limit controls future growth; it does not clean up existing history.

Disabling revisions entirely saves space but removes the only per-post undo. Restoring from a backup works at the site or database level, which is a poor fit for “I want yesterday afternoon’s version of this one post.” A modest cap is usually easier to live with than turning revisions off.

Checking and cleaning up

Everything can be checked read-only:

wp post list --post_type=revision --format=count
wp eval 'var_dump( defined( "WP_POST_REVISIONS" ) ? WP_POST_REVISIONS : "undefined" );'

Look at the total count and size compared with your posts, which posts hold the most revisions, and whether the limit is unlimited or filtered. If you decide to clean up:

  1. Back up first. Deleted revisions cannot be recovered.
  2. Set a limit first, or the history will simply build up again.
  3. Delete through WordPress (wp_delete_post_revision() or wp post delete), never with a raw SQL DELETE, which can leave orphaned metadata behind.
  4. Verify that key posts and the revisions screen still work.

Deleting rows often does not shrink the database file right away; reclaiming that space is what wp db optimize is for (see wp db check and wp db optimize).

Do revisions slow down the front end?

Mostly no. Front-end queries filter on post_type and post_status, and the composite type_status_date index excludes revision rows before they are read (see database index basics). Unlike autoloaded options, revisions are not read on every request. Their cost shows up in maintenance work: larger backups and migrations, a sluggish revisions screen when a post has hundreds of them, full-table operations such as search-and-replace, and database size limits on shared hosting.

Summary

Revisions are full copies stored in wp_posts, created only when an existing post is updated and its content changes, and each one captures the version after the save. On this blog, only the 31 posts updated after publishing have revisions, and posts created with WP-CLI kept no record of their first version. Autosaves overwrite one row per user, while auto-drafts and trash are cleaned up by WP-Cron. Revisions are the one thing that grows without limit by default, and WP_POST_REVISIONS only applies on the next update, so clean up in order: back up, set a limit, delete through WordPress, then verify.