Media Handling
Configure how Unpress handles your WordPress media files (images, videos, documents, etc.) during migration.
Overview
WordPress sites typically have hundreds or thousands of media files. Unpress offers three modes to handle these files:
- Local Download - Download media to your
site/folder - Reupload - Upload media to S3 or SFTP with automatic URL replacement
- Leave URLs - Keep original media URLs unchanged
Choose the mode that best fits your use case:
- Cost savings → Use Reupload (S3 is cheap)
- Archival → Use Leave URLs (preserve original structure)
- Simple migration → Use Local Download (easiest setup)
Mode 1: Local Download
Download media files from WordPress to your local site/ folder and update URLs in Markdown to point to these local files.
When to Use
- Quick migration with minimal setup
- Want to keep media with your site files
- Media files are small (under 100MB total)
- Deploying to platforms that support local files (Netlify, Vercel)
How to Enable
Add --download-media flag to your migration command:
pnpx @selfagency/unpress --download-media --generate-siteWhat Happens
- Unpress scans migrated Markdown files for media URLs
- Downloads each media file to
site/media/folder - Updates URLs in Markdown to point to local files:
- Before:
 - After:

- Before:
Output Structure
site/
├── media/
│ └── uploads/
│ ├── 2024/
│ │ ├── 01/
│ │ │ ├── image.jpg
│ │ │ └── document.pdf
│ │ └── 02/
│ │ └── photo.png
│ └── ...
├── content/
│ └── posts/
│ └── 2024-01-15-post.mdPros and Cons
| Pros | Cons |
|---|---|
| ✅ Easiest setup—no external accounts | ❌ Increases site size (media included) |
| ✅ Works offline after download | ❌ Longer deployment time |
| ✅ No ongoing costs | ❌ May hit file size limits on free hosting |
| ✅ Simple URL structure | ❌ Must redeploy to update media |
Troubleshooting
Media files not downloading:
- Check if WordPress media URLs are accessible
- Verify your internet connection is stable
- Look for error messages in migration output
Broken image links in deployed site:
- Ensure
site/media/folder was deployed - Check file permissions on media files
- Verify URL paths in Markdown are correct
Mode 2: Reupload to S3
Upload media files to Amazon S3 (or S3-compatible storage) and automatically update URLs in Markdown.
When to Use
- Large media libraries (100MB+)
- Want CDN performance and cheap storage
- Deploying to platforms with file size limits
- Want scalable, reliable media hosting
Prerequisites
- AWS Account - Create account at aws.amazon.com
- S3 Bucket - Create bucket to store media files
- AWS Credentials - Get Access Key ID and Secret Access Key
Step 1: Create S3 Bucket
- Log in to AWS Console
- Navigate to S3
- Click Create bucket
- Enter bucket name (globally unique, e.g.,
my-site-media-2024) - Choose AWS Region (closest to your users)
- Configure settings:
- Block Public Access settings: Off (to allow public media)
- Bucket Policy: Add policy to allow public read access
Step 2: Set Bucket Policy (Public Read)
In your S3 bucket, go to Permissions → Bucket Policy and add:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}Replace your-bucket-name with your actual bucket name.
Step 3: Get AWS Credentials
- Go to IAM → Users in AWS Console
- Create new user with Programmatic access
- Attach policy:
AmazonS3FullAccess(or create custom policy for your bucket) - Save Access Key ID and Secret Access Key (you won't see Secret Access Key again!)
Step 4: Configure Unpress for S3
Create a config file unpress.yml:
source:
type: api
api:
baseUrl: https://your-site.com
media:
mode: reupload
reupload:
driver: s3
s3:
bucket: your-bucket-name
region: us-east-1
endpoint: https://s3.amazonaws.comStep 5: Add AWS credentials to .env
Add your AWS credentials to the same .env file you use for Unpress:
AWS_ACCESS_KEY_ID=your-access-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-key
AWS_REGION=us-east-1Never commit credentials!
Never commit AWS credentials to Git or share them publicly. Use environment variables or secret management tools.
Step 6: Run Migration
pnpx @selfagency/unpress --config unpress.yml --generate-siteUnpress will:
- Download media from WordPress (to temporary folder)
- Upload each file to your S3 bucket
- Update URLs in Markdown to point to S3:
- Before:
 - After:

- Before:
What Gets Uploaded
- Images (JPG, PNG, GIF, SVG, WebP)
- Videos (MP4, WebM, MOV)
- Documents (PDF, DOC, DOCX)
- Audio files (MP3, WAV, OGG)
Pros and Cons
| Pros | Cons |
|---|---|
| ✅ Cheap storage (~$0.023/GB/month) | ❌ Requires AWS account and setup |
| ✅ Fast CDN via CloudFront (optional) | ❌ Must manage AWS credentials |
| ✅ Scales to any size | ❌ Has ongoing AWS costs |
| ✅ Faster site deployment (media not included) | ❌ External dependency on S3 |
| ✅ Better performance with CDN | ❌ More complex than local download |
Troubleshooting
"Access Denied" error:
- Check AWS credentials are correct
- Verify bucket policy allows public read access
- Ensure IAM user has S3 permissions
- Check bucket name and region match your config
"Bucket not found" error:
- Verify bucket name is correct (case-sensitive)
- Check bucket is in the correct region
- Ensure bucket exists and you have access
"Connection timeout":
- Check your internet connection
- Verify S3 endpoint URL is correct for your region
- Try increasing timeout (adjust in config if needed)
Optional: Use CloudFront CDN
For even better performance, set up CloudFront CDN in front of your S3 bucket:
- Go to CloudFront in AWS Console
- Create distribution with S3 bucket as origin
- Update
unpress.ymlwith CloudFront domain:
media:
mode: reupload
reupload:
driver: s3
s3:
bucket: your-bucket-name
region: us-east-1
endpoint: https://your-cloudfront-id.cloudfront.netURLs will point to CloudFront (faster, lower latency).
Mode 3: Reupload to SFTP
Upload media files to SFTP server and automatically update URLs in Markdown.
When to Use
- Existing SFTP hosting for media
- Want to use your own server for media
- Deploying to platforms that can't handle large media
- Prefer complete control over media hosting
Prerequisites
- SFTP Server - Server with SFTP access enabled
- SFTP Credentials - Host, port, username, password/key
- Directory Path - Path where media should be uploaded
Step 1: Configure Unpress for SFTP
Create a config file unpress.yml:
source:
type: api
api:
baseUrl: https://your-site.com
media:
mode: reupload
reupload:
driver: sftp
sftp:
host: your-server.com
port: 22
username: your-username
password: your-password
path: /var/www/mediaStep 2: Add SFTP credentials to .env (optional)
You can also place SFTP credentials in .env:
SFTP_HOST=your-server.com
SFTP_PORT=22
SFTP_USER=your-username
SFTP_PASSWORD=your-passwordStep 3: Run Migration
pnpx @selfagency/unpress --config unpress.yml --generate-siteUnpress will:
- Download media from WordPress (to temporary folder)
- Upload each file to your SFTP server
- Update URLs in Markdown to point to SFTP:
- Before:
 - After:

- Before:
SFTP Authentication Options
Password Authentication
sftp:
host: your-server.com
username: your-username
password: your-passwordSSH Key Authentication (Recommended)
sftp:
host: your-server.com
username: your-username
privateKey: /path/to/private/key.pem
passphrase: your-passphrase # OptionalPros and Cons
| Pros | Cons |
|---|---|
| ✅ Use your existing server | ❌ Requires server setup and maintenance |
| ✅ Full control over media hosting | ❌ Must manage server credentials |
| ✅ Can use any SFTP server | ❌ Server performance impacts media delivery |
| ✅ No external service costs | ❌ Scalability depends on your server |
| ✅ Faster site deployment | ❌ More complex than local download |
Troubleshooting
"Connection refused" error:
- Verify SFTP port is correct (default: 22)
- Check if SFTP service is enabled on your server
- Ensure firewall allows SFTP connections
- Verify hostname resolves correctly
"Authentication failed" error:
- Check username and password/SSH key are correct
- Verify SSH key format is supported (PEM format)
- Test SFTP connection manually:
sftp your-server.com
"Permission denied" error:
- Check if upload path exists and is writable
- Verify user has write permissions on target directory
- Check directory ownership and permissions
Mode 5: Reupload to SCP
Upload media files to a server via SCP (Secure Copy Protocol) and automatically update URLs in Markdown.
When to Use
- Direct file transfer without SSH access
- Minimal SSH service needed (SCP protocol)
- Simpler than SFTP for basic file uploads
- Server with SSH but prefer SCP over interactive shell access
- Want to use existing SSH infrastructure for file transfer
Prerequisites
- SSH Server - Server with SSH access enabled (SCP runs over SSH)
- SCP Credentials - Host, port, username, password/key
- Directory Path - Path where media should be uploaded
Step 1: Configure Unpress for SCP
Create a config file unpress.yml:
source:
type: api
api:
baseUrl: https://your-site.com
media:
mode: reupload
reupload:
driver: scp
scp:
host: your-server.com
port: 22
username: your-username
password: your-password
path: /var/www/mediaStep 2: Add SCP credentials to .env (optional)
You can also place SCP credentials in .env:
SCP_HOST=your-server.com
SCP_PORT=22
SCP_USER=your-username
SCP_PASSWORD=your-passwordStep 3: Run Migration
pnpx @selfagency/unpress --config unpress.yml --generate-siteUnpress will:
- Download media from WordPress (to temporary folder)
- Upload each file to your SCP server
- Update URLs in Markdown to point to SCP:
- Before:
 - After:

- Before:
SCP Authentication Options
Password Authentication
scp:
host: your-server.com
username: your-username
password: your-passwordSSH Key Authentication (Recommended)
scp:
host: your-server.com
username: your-username
privateKey: /path/to/private/key.pemPros and Cons
| Pros | Cons |
|---|---|
| ✅ Simple folder-based upload | ❌ Requires SSH setup and maintenance |
| ✅ Full control over media hosting | ❌ Must manage server credentials |
| ✅ Works on standard SSH servers | ❌ Cannot list server-side files |
| ✅ No external service costs | ❌ Server performance impacts media delivery |
| ✅ Faster site deployment | ❌ More complex than local download |
Troubleshooting
"Connection refused" error:
- Verify SCP port is correct (default: 22)
- Check if SSH service is enabled on your server
- Ensure firewall allows SSH/SCP connections
- Verify hostname resolves correctly
"Authentication failed" error:
- Check username and password/SSH key are correct
- Verify SSH key format is supported (PEM format)
- Test SCP connection manually:
scp ./file your-username@your-server.com:/tmp/
"Permission denied" error:
- Check if upload path exists and is writable
- Verify user has write permissions on target directory
- Check directory ownership and permissions
Mode 4: Leave URLs
Keep original WordPress media URLs unchanged. This is the simplest mode and ideal for archival.
When to Use
- Archival - Preserve original structure and links
- Minimal migration - Want simplest possible setup
- Existing CDN - WordPress already uses CDN for media
- Cost savings - Don't want to move media
How to Enable
Create a config file unpress.yml:
media:
mode: leaveOr just run Unpress with your existing .env and no media override flags:
pnpx @selfagency/unpress --generate-siteDefault behavior (if no media config) is to leave URLs unchanged.
What Happens
Unpress does not download or upload media. Media URLs in Markdown remain as they were in WordPress:
Your new site will continue loading media from your WordPress server.
Pros and Cons
| Pros | Cons |
|---|---|
| ✅ Easiest setup—no changes to media | ❌ Depends on WordPress server remaining online |
| ✅ Fastest migration—no media processing | ❌ No performance improvement for media |
| ✅ No external service needed | ❌ Can't delete WordPress server |
| ✅ Minimal site size | ❌ No cost savings from media hosting |
| ✅ Preserves original URLs | ❌ Media delivery speed unchanged |
When This Fails
Problem: WordPress server is offline or media is deleted.
Solution: Your new site will show broken image links. To fix this:
- Migrate again using Local Download or Reupload mode
- Or manually restore WordPress server/media files
Use Case: Archival Migration
For archival scenarios, this mode is ideal because:
- Preserves original structure exactly
- No risk of losing media if WordPress is archived properly
- Minimal migration effort
- Maintains historical accuracy
However, ensure your WordPress media is properly archived:
- Backup media files to reliable storage
- Consider using Wayback Machine or archive.org for public sites
- Document media structure for future reference
Choosing the Right Mode
Decision Flowchart
Do you want to move media?
├─ No → Use "Leave URLs" (easiest)
└─ Yes
├─ Want simplest setup?
│ └─ Yes → Use "Local Download"
└─ Want best performance/cost?
├─ Have AWS account?
│ └─ Yes → Use "Reupload to S3"
└─ Have SFTP server?
└─ Yes → Use "Reupload to SFTP"Comparison Table
| Mode | Setup Difficulty | Performance | Cost | Best For |
|---|---|---|---|---|
| Local Download | ⭐ Easy | ⭐⭐⭐ Good | $0 | Small sites, quick migration |
| Reupload to S3 | ⭐⭐⭐ Complex | ⭐⭐⭐⭐ Excellent | $0.023/GB/mo | Large sites, CDN performance |
| Reupload to SFTP | ⭐⭐ Medium | ⭐⭐⭐ Good | $0 (server cost) | Existing server, full control |
| Reupload to SCP | ⭐⭐ Medium | ⭐⭐⭐ Good | $0 (server cost) | Simple file transfer, SSH setup |
| Leave URLs | ⭐ Easiest | ⭐⭐ Fair | $0 | Archival, minimal migration |
Recommendations
For cost savings:
- Use Reupload to S3 (cheapest storage at scale)
- Local Download works for small sites (< 100MB media)
For archival:
- Use Leave URLs (preserve original structure)
- Ensure WordPress media is properly backed up
For performance:
- Use Reupload to S3 + CloudFront CDN (fastest)
- Reupload to fast SFTP server with caching
For simplicity:
- Use Leave URLs (no changes needed)
- Local Download if you want media with your site
Next Steps
After configuring media handling:
- Generated Site Guide - Understand your output structure
- Deployment Guide - Deploy your site (consider media mode!)
- WordPress API Migration - Run migration with media config
- XML Export Migration - Use XML with media reupload