What makes up a site
To move a WordPress site you need two things:
- Files: WordPress core (which you can download again), and the parts that are yours:
wp-content(themes, plugins, uploads) andwp-config.php. - The database: every post, page, setting, user and comment.
Copying only one of them gives you a broken site.
The address is stored in the database
WordPress stores its own address in the database, in more places than you might expect: the siteurl and home options, every link and image address inside post content, menu items, widget and plugin settings. Here our local site's address appears in content, as images and internal links normally do:
wp post create --post_title="Our bakery" --post_status=publish --post_content="<img src=\"http://localhost:8080/wp-content/uploads/bakery.jpg\" alt=\"Bakery\"> See our <a href=\"http://localhost:8080/menu/\">menu</a>."
wp option update bakery_settings '{"logo":"http://localhost:8080/wp-content/uploads/logo.png","hours":"9-6"}' --format=json
wp option get bakery_settings --format=jsonSuccess: Created post 4.
Success: Updated 'bakery_settings' option.
{"logo":"http:\/\/localhost:8080\/wp-content\/uploads\/logo.png","hours":"9-6"}The option was stored as a PHP array, so in the database it is serialized, a text format that records each string's length:
wp db query "SELECT option_value FROM wp_options WHERE option_name = 'bakery_settings'" --skip-column-namesa:2:{s:4:"logo";s:50:"http://localhost:8080/wp-content/uploads/logo.png";s:5:"hours";s:3:"9-6";}s:50:"http://localhost:8080/..." means "a string of 50 characters". If you replaced the address with https://bakery.example (a different length) using plain SQL, the recorded length would be wrong, PHP could no longer read the data, and the settings would silently vanish. That is why you must use a serialization-aware tool.
Changing the address safely
wp search-replace understands serialized data and fixes the lengths. Always run it with --dry-run first:
wp search-replace "http://localhost:8080" "https://bakery.example" --dry-run --report-changed-only+------------+--------------+--------------+------+
| Table | Column | Replacements | Type |
+------------+--------------+--------------+------+
| wp_options | option_value | 3 | PHP |
| wp_posts | post_content | 3 | SQL |
| wp_posts | guid | 4 | SQL |
| wp_users | user_url | 1 | SQL |
+------------+--------------+--------------+------+
Success: 11 replacements to be made.The report shows how many replacements each table and column would get. In wp_options: the siteurl and home options and our settings option (marked PHP because it was serialized data, handled specially). In wp_posts: links in post content (our post and the sample page, which links to the dashboard) and each post's guid column. And the administrator's website in wp_users.
The guid column is a permanent identifier that feed readers use to recognise posts. When moving a local site to its first live address, replacing it is harmless. On a site that is already live and has subscribers, many people add --skip-columns=guid so existing feed items are not seen as new. Without --dry-run the changes are made:
wp search-replace "http://localhost:8080" "https://bakery.example" --report-changed-only
wp option get home
wp option get bakery_settings --format=json+------------+--------------+--------------+------+
| Table | Column | Replacements | Type |
+------------+--------------+--------------+------+
| wp_options | option_value | 3 | PHP |
| wp_posts | post_content | 3 | SQL |
| wp_posts | guid | 4 | SQL |
| wp_users | user_url | 1 | SQL |
+------------+--------------+--------------+------+
Success: Made 11 replacements.
https://bakery.example
{"logo":"https:\/\/bakery.example\/wp-content\/uploads\/logo.png","hours":"9-6"}The serialized option now holds the new address with correct lengths, so it still reads back as normal data.
The full move, step by step
Moving from your computer to a live server:
- Prepare the server: create a database and a database user on the host, and point the domain at it.
- Export the database locally:
wp db export site.sql. - Upload the files:
wp-contentat least (or the whole folder) with SFTP,rsyncor your host's file manager. - Import the database on the server:
wp db import site.sql(or phpMyAdmin). - Edit
wp-config.phpon the server with the new database name, user, password and host, and fresh security keys fromhttps://api.wordpress.org/secret-key/1.1/salt/. - Search-replace the old address with the new one:
wp search-replace "http://localhost:8080" "https://www.example.com" --dry-run, then without--dry-run. - Flush rewrite rules and caches:
wp rewrite flush,wp cache flush. - Check: log in, click through pages, test forms, look for mixed-content warnings (images still on
http://), and untick Discourage search engines if it was set.
Many people use a migration plugin (such as All-in-One WP Migration, Duplicator or Migrate Guru) or their host's migration tool, which bundle these steps. Knowing the manual steps means you can fix things when an automated move fails part-way, which happens with large sites.
Local, staging and live
For sites that matter, use three copies:
- Local: on your computer, for building and experimenting.
- Staging: a private copy on the server (many hosts create one with a click), for testing updates and new features with real data.
- Live (production): what visitors see.
The golden rule: code moves up, content moves down.
- Theme and plugin code goes local → staging → live, ideally through Git and a deployment step.
- Content (posts, orders, comments, users) is created on live, so you copy the live database down to staging or local when you need fresh data.
Never copy a staging database up over a live shop or membership site: you would erase every order and sign-up made since the copy was taken.
HTTPS
Live sites must use HTTPS. Most hosts provide free certificates (Let's Encrypt). After enabling it, make sure both addresses in Settings → General start with https://, and search-replace any remaining http:// links to your own domain.
Common mistakes
- Plain SQL
REPLACE()on URLs, corrupting serialized settings. - Forgetting
wp-config.phpchanges on the new server. - Overwriting live content with an older database.
- Leaving "Discourage search engines" on after launch.
- No backup before the move.
Exercise
Your local site at http://mysite.local must move to https://www.mysite.com. Write the WP-CLI commands for the database part on the server, after the files and database have been uploaded and imported.
Show one possible answer
wp search-replace "http://mysite.local" "https://www.mysite.com" --dry-run --report-changed-only
wp search-replace "http://mysite.local" "https://www.mysite.com" --report-changed-only
wp rewrite flush
wp cache flush
wp option get homeThe last command should print https://www.mysite.com. Then log in at https://www.mysite.com/wp-admin/ and click through the site.
Key points
- A WordPress site is files (mainly wp-content and wp-config.php) plus the database; moving it means copying both.
- The database stores the site address in many places, including serialized data, so use a serialization-aware search-replace.
- Professional workflows use local, staging and live copies: code moves up, content moves down.
- Migration plugins and host tools automate the process; understanding the manual steps helps when they fail.