# 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`:

```yaml
# settings/deploy.md
---
mediaHosting: r2
mediaCdnUrl: https://cdn.example.com
---
```

Then configure your storage credentials from the CLI:

```bash
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](/docs/images#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.