Guide
Media and URL rewriting
What happens to images, documents and videos when an export downloads them: where the files land, which URL forms are recognised as the same asset, which fields get rewritten in each format, and how to take only the media the content actually uses.
When downloading media with --download-media, the exporter rewrites URLs in exported content to point to local files.
📁 File Organization
Downloaded media files are stored in a structured format, in a subfolder per media category
(images, videos, audio, documents, archives, code, other):
export/
├── posts/
│ └── my-post.md
├── pages/
│ └── about.md
└── media/
├── images/
│ ├── 123_featured-image.jpg
│ └── 124_inline-photo.png
├── documents/
│ └── 125_document.pdf
└── videos/
└── 126_video.mp4
Naming pattern: {media_id}_{original_filename}{extension}
🔄 URL Rewriting
Every reference to a downloaded attachment is rewritten — src, href, srcset and any
other URL occurrence are treated identically, so the export keeps working once the source
WordPress host is retired.
| Original URL | Rewritten Path |
|---|---|
https://example.com/wp-content/uploads/2025/01/photo.jpg |
/media/images/123_photo.jpg |
https://example.com/wp-content/uploads/2025/01/photo-300x200.jpg |
/media/images/123_photo-300x200.jpg |
https://example.com/wp-content/uploads/2025/01/photo-150x150.jpg |
/media/images/123_photo-150x150.jpg |
Files the media library does not list are salvaged. Page-builder renditions
(uploads/elementor/thumbs/…), attachments whose record was deleted while the file is still
served, and brand assets declared only in the document head never appear in /wp/v2/media —
so without this they stayed absolute and the migrated site hotlinked the source host. Every
same-host asset URL that content, SEO metadata or the marketing block references and the
library cannot account for is fetched into media/<kind>/ under a name prefixed with a short
hash of its source path (page builders repeat basenames across directories). A URL on a
foreign host is left alone — it is somebody else's file — and one that no longer resolves is
skipped rather than failing the export.
Matching is scheme- and host-insensitive. WordPress stores post_content with whatever URL
form was current when the post was written, while the REST API reports source_url in the site's
present-day form. All of these resolve to the same exported file:
| Reference form in content | Example |
|---|---|
| current form | https://example.com/wp-content/uploads/… |
| historic scheme | http://example.com/wp-content/uploads/… |
www / former domain |
https://www.example.com/…, http://old-domain.example/… |
| protocol-relative | //example.com/wp-content/uploads/… |
| root-relative | /wp-content/uploads/… |
| with a query string | …/photo.jpg?ver=2 |
URLs that do not correspond to a downloaded attachment are left untouched.
📐 Path Style: --media-path-style
| Value | Emitted path | When to use |
|---|---|---|
root (default) |
/media/images/123_photo.jpg |
Resolves identically from any URL depth — correct for a page served at /about/team/ |
relative |
media/images/123_photo.jpg |
Only correct for content served from the site root; kept for backwards compatibility with pre-1.7.9 exports |
1# Default — root-relative, works at any URL depth
2wpexportjson export --url https://example.com -f markdown --download-media
3
4# Pre-1.7.9 behaviour
5wpexportjson export --url https://example.com -f markdown --media-path-style relative
URL rewriting applies to the json and markdown formats only, and can be disabled entirely
with --keep-original-urls (other formats always keep original URLs, since the target platform
imports media from the live site).
📋 Per-Format URL Contract
What each format does with URLs, so you know what you are getting before you run an export:
| Format | Media URLs | Address fields (link, canonical_url) |
|---|---|---|
json |
localised to /media/… |
absolute (--link-style root to change) |
markdown |
localised to /media/… |
absolute (--link-style root to change) |
ssg |
localised to /media/… |
root-relative by default |
shopify, magento, wordpress, drupal, wix, squarespace, webflow, weebly, prestashop, ghost, strapi, contentful |
left absolute — the target platform imports media from the live site | absolute |
--keep-original-urls disables all rewriting for json, markdown and ssg.
🗂️ Which Fields Are Localised
| Field | Localised | Why |
|---|---|---|
body content (content.rendered) |
✅ | assets |
excerpt |
✅ | assets |
featured_image |
✅ | asset |
og_image |
✅ when it resolves | asset — but an og:image on a CDN or third-party host isn't a downloaded attachment, so it stays absolute |
canonical_url |
⚙️ --link-style |
address of the source site, not an asset |
link |
⚙️ --link-style |
as above |
hreflangs[].href |
⚙️ --link-style |
as above |
🔗 Address Fields: --link-style
link, canonical_url and hreflangs[].href are addresses of the source site, not assets, so
they are governed separately from media:
| Value | Emitted | When to use |
|---|---|---|
absolute (default) |
https://example.com/2010/07/21/389/ |
You need the original URL — to derive the target URL yourself, or because the old site stays up |
root |
/2010/07/21/389/ |
You are rebuilding the site at the same paths. Preserves each URL (and its search ranking) on the new host without pinning content to the old one |
1# Rebuilding at the same paths on a new host
2wpexportjson export --url https://example.com -f markdown --link-style root
Only same-host addresses are converted. An hreflang alternate or canonical pointing at a
different host keeps pointing where it points. Query strings and fragments are preserved
(/a/?page=2#top).
📷 Size Variants
WordPress generates multiple image sizes (thumbnail, medium, large, full). The exporter:
- ✅ Downloads the original full-size image and every registered size variant
- ✅ Rewrites each variant URL to its own exported file, preserving responsive
srcset - ✅ Handles
-{width}x{height}suffixed URLs automatically - ✅ Remaps stale variants: a registered-size change regenerates thumbnails but never
rewrites the markup already linking to the old dimensions. A reference to a
no-longer-generated
photo-300x199.jpgis remapped to the closest surviving width (photo-300x225.jpg) instead of being left as a dead path. Run with--verboseto see each remap.
🎯 Selective Media with --relevant-media-only
For sites with large media libraries, use --relevant-media-only to download only used media:
1wpexportjson export --url https://example.com --relevant-media-only -f markdown
What gets downloaded:
| Media Type | Downloaded | Condition |
|---|---|---|
| Featured images | ✅ Yes | Referenced by featured_media field |
| Content images | ✅ Yes | Found in <img> tags within content |
| Excerpt images | ✅ Yes | Found in <img> tags within excerpt |
| Linked PDFs/documents | ✅ Yes | Found in <a href> tags (pdf, docx, xlsx, etc.) |
| Linked videos | ✅ Yes | Found in <a href> tags (mp4, webm, avi, etc.) |
| Linked archives | ✅ Yes | Found in <a href> tags (zip, rar, 7z, etc.) |
| Unused library items | ❌ No | Not referenced by any post/page |
Benefits:
- 📉 Significantly reduces export size
- ⚡ Faster export for content-heavy sites
- 🎯 Only relevant assets are included (images, documents, videos)
💡 Examples
1# Download all media (default)
2wpexportjson export --url https://example.com -f markdown
3
4# Download only featured images and content images
5wpexportjson export --url https://example.com --relevant-media-only -f markdown
6
7# Skip media download entirely
8wpexportjson export --url https://example.com --no-media -f markdown
9
10# Combine with path filter for targeted export
11wpexportjson export --url https://example.com --path-filter=/blog/ --relevant-media-only -f markdown