Anatomy of an Exported Website
When a website owner runs Developer → Export they get back a .zip with one of two shapes. This page describes the structure of those bundles so you can build, host, and continue developing the site without r2ware in the picture.
For the user-facing walkthrough, see Exporting Your Website.
Two bundle shapes
Both shapes ship as <slug>-YYYY-MM-DD-<sha8>.zip — the date is when the export ran and <sha8> is the first eight chars of the source commit. shutil.make_archive archives the contents of the tree, so when you unzip you'll get files in the current directory; create an enclosing folder first if you want one.
Static bundle
A snapshot of the website's most recent live build output. Contents look like a built Jekyll site:
./ ├── index.html ├── about/index.html ├── blog/ │ └── ... ├── assets/ │ ├── css/main.css │ ├── js/... │ └── uploads/... # only present if "Include media files" was checked ├── feed.xml ├── sitemap.xml └── .nojekyll # tells GitHub Pages not to rebuild with JekyllThere is no _layouts/, _includes/, _config.yml, Gemfile, or node_modules — everything is already rendered. Serve it with any static file server.
Jekyll source bundle
The full source tree at the export's source SHA, plus generated build-and-deploy plumbing:
./ ├── _config.yml ├── _data/ ├── _includes/ ├── _layouts/ ├── _posts/ ├── _pages/ # if this site uses it; the tree is exported verbatim ├── assets/ │ └── uploads/ # only populated if "Include media files" was checked ├── .github/ │ └── workflows/ │ └── jekyll.yml # generated — builds & deploys to GitHub Pages └── Gemfile # generated — pins jekyll + the plugins this site usesThis is what you push to GitHub, GitLab, or any git host. Anyone with Ruby installed can clone it and run bundle exec jekyll serve.
The two checkboxes
Only two user choices feed into the bundle:
- Bundle kind — static (built output) vs. Jekyll (source).
- Include media files — when on, the export downloads every CDN-hosted file referenced in the site and bundles them locally under
assets/uploads/, rewriting URLs to relative paths. When off, those URLs keep pointing at r2ware's CDN.
That's it. Everything else (Gemfile contents, workflow file, transforms) is automatic.
The generated files
Gemfile (Jekyll bundle only)
The export reads the plugins: key from _config.yml, intersects it with a known-safe allowlist (jekyll-feed, jekyll-sitemap, jekyll-redirect-from, jekyll-seo-tag, jekyll-paginate), and pins each one. Example output:
source "https://rubygems.org"
gem "jekyll", "~> 4.3"
gem "webrick", "~> 1.8"
group :jekyll_plugins do
gem "jekyll-feed", "~> 0.17"
gem "jekyll-sitemap", "~> 1.4"
gem "jekyll-seo-tag", "~> 2.8"
end
If your site uses a plugin not on the allowlist, it's omitted from the Gemfile. The omission is silent in the bundle itself — diff your _config.yml plugins list against the generated Gemfile to spot what was dropped, then add any you need back manually.
.github/workflows/jekyll.yml (Jekyll bundle only)
A standard GitHub Pages-via-Actions workflow:
name: Build and deploy Jekyll site
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.2"
bundler-cache: true
- run: bundle exec jekyll build
env:
JEKYLL_ENV: production
- uses: actions/upload-pages-artifact@v3
with:
path: ./_site
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: $
steps:
- id: deployment
uses: actions/deploy-pages@v4
You can edit it freely after export. Common reasons to:
- Switch to deploying somewhere other than GitHub Pages (Netlify, Vercel, S3) — replace the
deployjob with the deploy step for your target. - Add a build matrix for multiple Jekyll versions.
- Add a pre-build step (e.g. compile Tailwind, run image optimization).
Finding what changed
The export doesn't ship a standalone audit file. Instead, each neutralized region gets an HTML comment starting with <!-- r2ware-export: ...:
- Forms — the
actionattribute is rewritten in place to a Formspree placeholder, and the explanatory comment is appended at the bottom of the file. - Shop includes and e-sign includes — the entire
{% include … %}is replaced by the comment, inline at the original position. data-r2ware-shopattributes — silently stripped (no comment)._admin.yml— deleted; no marker is left.
Grep the bundle to find every form/shop/e-sign touchpoint:
grep -rn "r2ware-export:" .
Transforms applied during export
For each bundle kind, the export pipeline applies these mechanical transforms. Each one is non-destructive on the r2ware side — only the export tree is modified.
| Transform | Applies to | What it does |
|---|---|---|
| Gemfile generation | Jekyll bundle | Writes a pinned Gemfile based on _config.yml plugins (allowlist) |
| Workflow generation | Jekyll bundle | Writes .github/workflows/jekyll.yml |
.nojekyll marker |
Static bundle | Tells GitHub Pages not to re-build with Jekyll |
| Decap config sanitization | Jekyll bundle | Drops _admin.yml (the Decap config was r2ware-managed) |
| Form endpoint neutralization | Jekyll bundle | Any HTML with action="/forms/submit" or action="" is rewritten to a Formspree-style placeholder with an explanatory HTML comment |
| Shop / e-sign neutralization | Jekyll bundle | {% include r2ware/cart.html %}, {% include r2ware/checkout.html %}, {% include r2ware/product... %}, and data-r2ware-shop="..." attributes are replaced with static stubs. E-sign includes ({% include r2ware/signature... %}, {% include signature-pad.html %}) get the same treatment. Layout files are not modified — only the includes are. |
| Secret scrubbing | Jekyll bundle | _data/*.yml top-level keys matching (secret|token|api[_-]?key|password) (case-insensitive) are removed; _config.yml r2ware.private: subtree is stripped |
| Media URL rewriting | Both bundles, when "Include media" is checked | Scans rendered HTML/CSS/XML/JSON/TXT for CDN URLs (using the export tree itself for static, or <rootdir>/live for jekyll), downloads each into assets/uploads/, rewrites references to /assets/uploads/.... For jekyll bundles, URLs that appear in rendered output but not verbatim in any source file are Liquid-synthesized — they're collected on the TransformReport but not surfaced in the bundle itself. You'll spot them post-export because the corresponding images bundle correctly but the URL in your rendered output still points at the CDN; fix the Liquid that builds the URL (typically a _config.yml value like cdn_base). |
The full pattern list lives in app/sites/export_patterns.py on the platform side — if you need a transform extended for your site's case, that's the file to look at.
Building locally after export
For the Jekyll bundle:
# Once
bundle install
# Each time you want to preview
bundle exec jekyll serve
# To produce the same output GitHub Actions will produce
JEKYLL_ENV=production bundle exec jekyll build
Output lands in _site/.
Pushing to GitHub yourself
The export gives you a zip — it does not push to GitHub for you. You'll do that manually:
# Replace the filename with whatever your export produced:
# <slug>-YYYY-MM-DD-<sha8>.zip
mkdir mysite && cd mysite
unzip ../mysite-2026-05-20-a1b2c3d4.zip
git init
git add .
git commit -m "Initial commit from r2ware export"
git branch -M main
# Create the repo on GitHub first, then:
git remote add origin git@github.com:YOUR-USER/YOUR-REPO.git
git push -u origin main
After that, on GitHub: Settings → Pages → Source: GitHub Actions. The workflow file we included handles the rest.
(A future release will add an optional "push to GitHub from r2ware" mode that does this for you and keeps the platform copy in sync. Until then, manual push is the path.)
Hosting checklists (quick reference)
The full walkthrough for each is in the Exporting Your Website guide. Quick summary:
| Host | Bundle | Notes |
|---|---|---|
| GitHub Pages | Either | Static: Settings → Pages → Deploy from branch. Jekyll: Settings → Pages → GitHub Actions. Watch baseurl. |
| Netlify | Either | Static: drag onto netlify.com/drop. Jekyll: connect repo, build command bundle exec jekyll build, publish dir _site. |
| Cloudflare Pages | Either | Static: Direct Upload. Jekyll: connect repo, framework preset Jekyll. |
| Vercel | Jekyll | Connect repo, framework preset Jekyll, build command bundle exec jekyll build, output _site. |
| S3 + CloudFront | Static | aws s3 sync the bundle, configure bucket for website hosting, point CloudFront at the website endpoint. |
| Self-hosted (nginx, caddy) | Either | Static: serve the directory. Jekyll: build on the server, point web server at _site/. |
Customizing what's exported
You can pre-commit your own Gemfile and .github/workflows/jekyll.yml to the r2ware site before exporting — the platform detects existing files at those paths and leaves them alone rather than overwriting. This is the right move if you've customized your build pipeline.
The transform list is fixed in the platform for now (no per-export opt-outs in the UI), but the patterns are all in app/sites/export_patterns.py if a transform misfires on your site's particular setup. File an issue and we'll adjust the pattern.