Media Hosting
Serve your site's media files from a CDN instead of your hosting platform's origin. sitemd syncs media/ to object storage at build time and rewrites all /media/ paths in the HTML output to point to your CDN URL.
How it works
When mediaHosting is configured, the build pipeline:
- Computes a SHA256 hash for every file in
site/media/ - Compares against a local manifest (
.media-manifest.json) to find new or changed files - Uploads only the changed files to R2 or S3
- Rewrites every
/media/...reference in the HTML output tohttps://your-cdn.com/media/...
All src, href, url(), and srcset references are rewritten — so galleries, image rows, and CSS background images all point to the CDN automatically.
Setup
Add two fields to settings/deploy.md:
# settings/deploy.md
---
mediaHosting: r2
mediaCdnUrl: https://cdn.example.com
---
Then configure your storage credentials from the CLI:
sitemd config setup hosting
This walks you through entering your provider credentials. Credentials are stored in .sitemd/config.json (gitignored).
Providers
Cloudflare R2
R2 uses the S3-compatible API — no egress fees, and pairs naturally with Cloudflare Pages deployments.
Credentials needed:
| Key | Where to find it |
|---|---|
r2AccountId |
Your Cloudflare account ID |
r2AccessKey |
R2 → Manage API Tokens → Create API Token |
r2SecretKey |
Shown once when creating the R2 token |
r2Bucket |
The bucket name you created in the R2 dashboard |
Set mediaHosting: r2 in settings/deploy.md. The CDN URL is typically the public bucket URL or a custom domain you've connected in R2 → Settings → Custom Domains.
CI env vars: SITEMD_R2_ACCOUNT_ID, SITEMD_R2_ACCESS_KEY, SITEMD_R2_SECRET_KEY, SITEMD_R2_BUCKET
AWS S3
Standard S3 object storage. Set up a bucket with public read access and optionally connect a CloudFront distribution in front of it.
Credentials needed:
| Key | Where to find it |
|---|---|
s3Region |
AWS region, e.g. us-east-1 |
s3AccessKey |
IAM user → Security credentials → Access keys |
s3SecretKey |
Shown once when creating the access key |
s3Bucket |
Your S3 bucket name |
Set mediaHosting: s3 in settings/deploy.md. For the mediaCdnUrl, use your CloudFront distribution URL or the S3 static website endpoint.
CI env vars: SITEMD_S3_REGION, SITEMD_S3_ACCESS_KEY, SITEMD_S3_SECRET_KEY, SITEMD_S3_BUCKET
Settings reference
Both settings go in settings/deploy.md frontmatter:
| Setting | What it does |
|---|---|
| mediaHosting | false disables hosting sync. r2 or s3 enables it for the respective provider. |
| mediaCdnUrl | The base URL for rewriting /media/ paths. No trailing slash. Example: https://cdn.example.com |
URL rewriting
When mediaCdnUrl is set, the build rewrites every reference to a /media/ path in the HTML output:
| Before | After |
|---|---|
src="/media/content/photo.jpg" |
src="https://cdn.example.com/media/content/photo.jpg" |
srcset="/media/content/photo.webp" |
srcset="https://cdn.example.com/media/content/photo.webp" |
url('/media/content/bg.png') |
url('https://cdn.example.com/media/content/bg.png') |
Rewriting happens on the final HTML files in the build output — your markdown source files are not modified.
Combining with image optimization
Use image optimization and media hosting together for maximum performance. The build pipeline runs them in order:
- Images are resized and converted to WebP (
imageOptimization: optimize) <picture>tags are written with WebP sources- All media files (including
.webpvariants) are synced to the CDN - HTML URLs are rewritten to the CDN prefix
The result: visitors get WebP images served from a CDN, with original-format fallback — without any manual steps.
Incremental syncs
The build keeps a manifest at site/.media-manifest.json tracking the SHA256 hash of every uploaded file. On subsequent builds, only files whose content has changed are re-uploaded. Large unchanged assets like videos or high-res images are skipped automatically.
The manifest file is in your build output directory (site/) and is not committed to git.