When we migrate a WordPress site, we copy the database from the previous server to the new one and then switch the DNS. The old site keeps taking writes (edits, comments, form entries, orders) until the switch is completed and they get lost. We can put the website in the maintenance mode, the maintenance mode would hide the whole site. We can also put the website in read-only mode, the site would look as it looks in normal mode but no data would be written to the old site.
How does a migration lose data?
Database export is basically a snapshot of the data at the time of exporting, the old site keeps running and writing data to the database after the export. But when we import the exported file to the new server, it never even looks at the further writes made to the database.
The final step remains to point our domain towards the new server, this can be done by changing the DNS records for the domain.
The whole process is not expected to be instantaneous, as the DNS records are cached at the DNS resolvers for a certain amount of time defined in the Time To Live (TTL) parameter (RFC 1035).
For e.g. if the TTL of the DNS record is set to 3600 (seconds) -> the record would be cached for 60 minutes and the clients would be seeing the old site till the cache expires on their end.
A very high TTL may cause some issues, for example if an order is made for an e-commerce website with the TTL value of 1 hour -> the order is placed on the server we delete in 1 hour.
Where else does this happen?
- The new host might have a new server or might be the same host just added to a new stack.
- The staging database might have been pushed over the live database.
- A backup of the production database might have been restored to the live database.
- The domain might have been changed on a copy and then swapped in.
There’s a rule for the same:
Only one database replaces another.
This means if we try to write to the losing copy after exporting it, it would be gone.
What gets written?
There can be many things when an editor is finishing a post and hitting ‘Update’ -> somewhere in between the post is being autosaved.
- comments
- contact form entries
- sign-ups
- WooCommerce orders and other order updates (via payment status updates)
- scheduled posts published via WP-Cron
- settings changed by another admin
Every time a user logs into WordPress, it creates a session token which is stored inside the session_tokens user meta.
We can spawn a WP-Cron even if we are just loading a normal page. A transient named doing_cron is created which marks that the cron is running and then it is deleted.
What about maintenance mode?
The WordPress core has one by default, which is located at the root of the WordPress site (.maintenance).
The updater script writes this file with the content <?php $upgrading = <timestamp>; ?> and deletes it when the upgrade is complete.
This is checked in wp-settings.php before the database loads.
If the file is found, the whole site goes into the maintenance mode with the message Briefly unavailable for scheduled maintenance. Check back in a minute.
This results in a 503 error with Retry-After: 600 in the headers.
This maintenance mode drops the whole site for both the visitors and the administrators.
The file is also ignored if the value of the $upgrading variable is older than 10 minutes.
So basically all the maintenance scripts you write would surely be done by the time of 10 minutes (assuming the migrations are not too big).
Other ways to freeze a site
- maintenance plugins -> we can also use a maintenance plugin which can show a placeholder page for all the visitors to the site.
- block user accounts -> we can create a plugin which can disable user accounts for certain users. From WPBeginner guide (User Blocker) it says that this would not stop the comments, forms, etc. getting sent to the database.
- constant
DISALLOW_FILE_MODS-> WordPress will not allow any plugin, theme or core file to be installed or updated directly via the WordPress UI. - read-only database user -> if we set our database user to read-only any action which needs to write to the database would fail and the user will not be able to log in to the admin panel, not even as an administrator.
What does read-only mean?
The reads would keep working, but every door would be closed for the write requests. The wp-admin panel and the REST API (4 methods: POST, PUT, PATCH, DELETE) won’t be able to write to the database.
The front end actions that are mainly comment actions, checkout, contact forms etc. would be blocked and then the XML-RPC requests, WP-Cron tasks and the automatic updates would not be able to execute as well.
The administrator login page wp-login.php would never be blocked, so an administrator can always log in and unfreeze your WordPress site.
Which status codes?
If a visitor tries to fill a form on a post -> We will get 503 Service Unavailable with a Retry-After header.
The status code 503 is defined in RFC 9110 as a temporary overload condition or for scheduled maintenance.
This would not affect page views, only the form posts would be hit with this status code.
Logged in as a user and trying to write to the post -> We would get 423 Locked error, this states that the page is locked, from WebDAV, refer RFC 4918.
Stripe retries failed webhooks for up to 3 days in live mode.
Freezing with Axle Read-Only Mode
Read-Only Mode
Freeze the site during a migration or deploy. Visitors keep reading; nothing written gets lost.
Use it when you move the site to a new host.
Learn more about Read-Only ModeTry it live: Read-Only ModeThe free plugin allows us to set the whole site to read-only mode from the Tools > Read-Only Mode menu or from the admin bar.
We can also invoke the read-only mode from WP-CLI by running the command wp axle-read-only-mode on --minutes=30 --reason="Moving servers".
In this state, several things happen:
- In the block editor, the Save and Publish buttons become disabled and the reason is shown.
- Open editors are locked within a minute via a heartbeat request.
- For visitors, the pages are still served normally, but if someone is trying to comment or post anything, they will be taken to a special “back soon” page.
- All the scheduled tasks are held and are executed when the read-only mode ends.
- WP-CLI commands will never be blocked, this means the import scripts and other CLI dependent scripts will still work.
Every blocked attempt would be listed under the Tools > Read-Only Mode section with who, when and what.
What does a freeze interrupt?
The plugin displays a freeze screen with the “Before you freeze” card, which has a list of what a freeze would interrupt right now.
This lists out the people editing, who has a post open, which post is open. Here “open” means the post lock is taken in the last 150 seconds.
All the posts which are scheduled to be published during the freeze period would be published late, when the freeze period ends.
Same thing goes for the WooCommerce orders, all the orders created in the last 2 hours and which are still unpaid would be included here.
Same thing goes for scheduled tasks, all the tasks which are scheduled to be run during the freeze period would be held until the freeze ends.
The card only informs us of the things which would be interrupted, it never stops the freeze.
The information for this is the same as the one we get for the scripts by running the command wp axle-read-only-mode check --minutes=30.
Scopes and exceptions
Each freeze has a scope, which means what would be allowed to write even after freezing.
- Everything: With this scope, nothing can be saved, this is the default scope if nothing is specified.
- Content only: With this scope, only content is frozen, which includes posts, comments, media, and other things which are connected to the content on your site. Options, plugins, and themes remain editable.
- Settings and code only: With this scope, only the things which are related to the settings or the codebase of the WordPress site would be frozen, so even if you are changing the post content, it would be saved.
- allow-list: In this you can keep some writes open, some examples of it would be
/wc/v3/webhooks(for webhook),?wc-api=, ajax action or user.
Webhook on the allow-list only if its provider does not retry
There’s also an option in the freeze window which says “Let me keep editing”, this would exempt the person from the freeze.
A server move, step by step
The day before the actual migration, the DNS TTL value for the domain must be lowered to a lower value so that when the customers try to access the website it would point to the new server within the time the TTL is expected to be there.
After that, the old server can be frozen by adding this line define( 'AXLE_READ_ONLY_MODE', true ); to its wp-config.php file. The constant is not saved in the database so the database export will not be frozen on the new server. If we are copying over the whole set of files, including the wp-config.php, we can simply remove this line from the wp-config.php on the new server.
A freeze from the screen is represented as the axle_read_only_mode_state option which travels with the export as well.
On the new server after the freeze is done, we can run the command wp axle-read-only-mode off to disable the freeze mode on the new server.
Steps to switch server:
- Export the site
- Copy the files to the new server
- Import the export to the new server
- Test everything out on the new server
- Switch the DNS
Switching the DNS of the site to the new server will make the old server appear frozen to the user until the server is shut down.
This gives us the advantage of knowing the edits which have been blocked on the old server and we can redo them on the new one.
If you want to test the migration process without affecting your live site, you can use a temporary domain (Axle Staging Guard -> staging) for the new server.
Limits
If our forms are posting straight to an outside service, this would not be affected.
The read-only mode will be available on every site where the plugin is active.
The read-only mode will end when the freeze period is over. It cannot be left in an active state even if a script fails to end it.
If the site is in read-only mode for more than 30 minutes past the end time, it is considered critical, and Site Health will flag it as such.
It is also marked critical if the site is under constant freeze because of the wp-config.php constant being set to force it, which is expected on the old server.
The read-only mode can be tested on try.axlewp.com/axle-read-only-mode.