Sadat Content Migration

Description

Sadat Content Migration is a selective WordPress content migration tool. It moves the content you choose and its portable dependencies without cloning themes, plugins, users, database tables, or the entire website.

Use it when you need to transfer blog posts, move pages between environments, copy a custom post type, or hand a controlled content package to another WordPress administrator.

Two migration workflows

  • Download and upload a verified ZIP migration package.
  • Send a completed package directly from one WordPress site to another with a short-lived, single-use connection key.

Portable content and settings

  • Posts, pages, and registered custom post types.
  • Same-type parent items required to preserve hierarchical page or custom-post relationships.
  • Titles, slugs, content, excerpts, dates, status, parent relationships, menu order, discussion settings, and sticky-post state.
  • Featured images, attached media, inline media, classic galleries and playlists, and Gutenberg image, gallery, audio, and video references.
  • Categories, tags, custom taxonomies, ancestor trees, descriptions, and term metadata.
  • Author mapping by login with a configurable local fallback author.
  • Portable custom fields and metadata-based SEO settings.
  • Comments, plus optional comment metadata and personal data when an administrator explicitly selects them.
  • Optional post passwords when an administrator explicitly selects them.

Reliable imports

  • Media, post creation, and relationship finalization run in small AJAX batches.
  • Source-to-destination ID and URL maps repair internal links, media markers, block attributes, featured images, and parent relationships.
  • Existing migrated content can be updated, skipped, or copied.
  • Matching media can be reused by source identity or SHA-256 file hash.
  • Custom post types and taxonomies are imported only when they are registered on the destination.
  • Package paths, MIME types, file sizes, archive expansion, and SHA-256 hashes are validated before import.
  • Temporary jobs are isolated, expire automatically, and include administrator-readable logs.

SEO metadata

The plugin transfers portable post and term metadata, including metadata keys commonly used by Yoast SEO, Rank Math, SEOPress, The SEO Framework, and similar plugins. Vendor-specific custom database tables, global plugin options, redirects, and analytics data are not migrated. Install and configure the same SEO plugin on the destination before importing when you need its fields to be interpreted there.

Privacy and data handling

Sadat Content Migration does not collect analytics, add tracking, or contact the developer’s website. Migration packages can contain private content or personal data if an administrator selects those options. Treat downloaded packages as confidential and delete them when the migration is complete.

Temporary packages and processing files are stored below the current site’s WordPress uploads directory, in sadat-content-migration/site-ID/, and removed according to the retention setting. Fixed Apache and IIS deny rules protect this directory. Before a job starts, the plugin checks a harmless random text file through the site’s uploads URL and requires an HTTP 403 or 404 response. A successful check is cached for five minutes. If protection cannot be verified, the job does not start. Imported content receives private source identifiers used only for duplicate detection and later updates.

External services

The plugin has no third-party service dependency. It makes a loopback request to its own harmless storage probe to verify that migration files cannot be downloaded directly; no content or personal data is included in this request.

When an administrator starts a direct transfer, the source site contacts only the destination WordPress URL entered by that administrator. It sends package metadata and the selected migration ZIP in sequential chunks to the Sadat Content Migration REST endpoints on that destination. The request uses the short-lived connection key generated by the destination administrator. Nothing is sent until both administrators take those explicit actions.

Screenshots

Installation

  1. Install and activate Sadat Content Migration on the source and destination WordPress sites.
  2. Open Tools > Content Migration.
  3. On the source, choose the content filters and create a migration package.
  4. Download the ZIP, then open the Import tab on the destination and upload it.
  5. Review duplicate, author, date, status, comment, and password options before starting the import.

For a direct transfer, enable inbound transfers in destination settings, generate a 30-minute key, and enter the destination URL and key on the source site.

FAQ

Does this clone an entire website?

No. It selectively migrates content and portable relationships. It does not move themes, plugins, users, site options, arbitrary database tables, or server configuration.

Why was a custom post type or taxonomy skipped?

The same theme or plugin that registers that post type or taxonomy must be active on the destination before import.

Will it preserve every SEO setting?

It preserves portable post and term metadata, including common SEO fields. Global settings and data stored only in a vendor-specific custom table are outside this plugin’s scope.

How are duplicates detected?

Imported posts and media receive a private identifier based on the source site and original object ID. Media can also be reused when its SHA-256 file hash matches an existing imported file.

Are internal links and image IDs updated?

Yes. After all destination objects have IDs, the finalization phase rewrites known source URLs, WordPress media classes, block attributes, featured-image IDs, parent IDs, and ID-oriented custom-field values.

Can it migrate comments safely?

Comments are optional. Email addresses, IP addresses, user agents, and comment metadata require a separate explicit export and import choice because plugin-added comment metadata can contain personal data.

Why did published content import as a draft?

The importing user must have the destination post type’s publishing capability. Otherwise, publish, future, and private states are safely downgraded to draft.

Which media files can I migrate?

The plugin accepts common raster images, audio, video, PDF, text, and office documents that WordPress also allows on the current site. Scripts, executables, HTML, SVG, server configuration files, archives, and executable double extensions are rejected. Unsupported source attachments are skipped with a log entry. Every ZIP must contain only the package manifest, content data, media index, and the exact media files declared in that index. Older packages remain supported only if they meet these checks.

Why does the plugin say private storage could not be verified?

The host must block direct HTTP access to the sadat-content-migration directory within the current site’s uploads directory. Apache and IIS rules are created automatically. On Nginx, or when directory rules are disabled, ask the hosting provider to configure the corresponding deny rule. The site also needs to reach its own uploads URL without authentication or a redirect so it can verify the protection. Do not edit plugin files to bypass this check.

What happens to unfinished jobs when updating from 1.0.4?

Start a new export or import after updating. Jobs from the previous temporary-storage location are not resumed. Previously downloaded packages can still be imported if they pass the stricter validation. Old server temporary files are left to the host’s temporary-file cleanup.

Can I transfer very large sites?

The plugin uses batches to reduce request timeouts, but PHP upload, memory, disk, and web-server limits still apply. Narrow the export filters or split a large migration into several packages when needed. The PclZip fallback supports individual files up to 32 MB; enable the PHP ZipArchive extension for larger files.

Does direct transfer work on local or private-network sites?

The outbound request uses WordPress safe HTTP validation to reduce server-side request forgery risk. A public HTTPS destination is required. Use the download/upload workflow for local development, HTTP-only, or private-network destinations.

Does it support WordPress multisite?

It operates on the current site in a multisite network. It does not migrate an entire network in one operation.

Reviews

There are no reviews for this plugin.

Contributors & Developers

“Sadat Content Migration” is open source software. The following people have contributed to this plugin.

Contributors

Changelog

1.0.5

  • Replaced bulk ZIP extraction with bounded, allowlisted entry copying after full archive inspection.
  • Reject executable files, double extensions, unsafe paths, symlinks, encrypted entries, duplicate paths, undeclared media, and inconsistent manifests before extraction.
  • Apply media filename and WordPress file-type checks on export and import, and verify each extracted file’s exact size and SHA-256 hash.
  • Move job files to protected, site-specific WordPress uploads storage and fail closed if direct-access protection cannot be verified.
  • Validate raw archive paths in the PclZip fallback and prevent its use of external temporary files.

1.0.4

  • Renamed the plugin and requested the distinctive sadat-content-migration directory slug.
  • Updated the plugin homepage URL and text domain throughout the admin and REST interfaces.
  • Added a new migration package identifier while retaining validation support for packages created by earlier releases.

1.0.3

  • Changed uploaded migration-package handling to use the WordPress file uploader while preserving isolated job storage and archive validation.
  • Improved repository-check compatibility for intentional binary streaming, private temporary-file permissions, and read-only admin navigation.
  • Improved duplicate-detection query compatibility and updated the plugin homepage URL.

1.0.2

  • Fixed: the “Download migration package” link always failed with WordPress’s “The link you followed has expired” message, even immediately after a successful export. The finalize step was building the link with wp_nonce_url(), which HTML-escapes “&” to “&” for safe use inside markup; the link was instead being assigned via JavaScript (link.href = ...), which never decodes that escaping, so the job ID and nonce were never actually received by the server. The link is now built without HTML escaping, since it is delivered as JSON and consumed by JavaScript, not printed into HTML.

1.0.1

  • Fixed: export package creation could hit a fatal “maximum execution time exceeded” error on hosts with a low PHP execution-time limit, which prevented the download link from ever completing. The finalize, package-build, and package-extract steps now request more execution time where the host allows it.
  • Fixed: the package download response now discards any stray output before sending the ZIP, and streams the file in chunks, so a downloaded package can no longer be corrupted by unrelated output from another active plugin or theme.

1.0.0

  • Initial release.
  • Added selective package export and verified batch import.
  • Added media discovery, taxonomy trees, custom fields, SEO metadata, author mapping, comments, and post settings.
  • Added duplicate strategies, ID and URL rewriting, logs, integrity validation, and direct site-to-site package transfer.