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:

  1. Computes a SHA256 hash for every file in site/media/
  2. Compares against a local manifest (.media-manifest.json) to find new or changed files
  3. Uploads only the changed files to R2 or S3
  4. Rewrites every /media/... reference in the HTML output to https://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:

  1. Images are resized and converted to WebP (imageOptimization: optimize)
  2. <picture> tags are written with WebP sources
  3. All media files (including .webp variants) are synced to the CDN
  4. 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.