This post explains how to set up a static site on Cloudflare R2.
I recently migrated my blog to Cloudflare R2, and the process went smoothly until I encountered R2’s lack of native support for rewriting URLs to index.html files. This post explains how I resolved this issue.
Setting Up
- Create the bucket.
- Add the custom domain
blog.iany.me. To redirectiany.meandwww.iany.meto the blog, add these as custom domains as well. Since I host the domain in Cloudflare, the DNS record is automatically configured.
Publishing
I use rclone to deploy the site to R2. Below is the GitHub workflow I employ. Noting that the fetch-depth option for actions/checkout retrieves the complete repository history. This works with Hugo’s --enableGitInfo flag to accurately determine article creation dates.
name: Deploy Hugo Site to Cloudflare R2
on:
push:
branches:
- master
workflow_dispatch:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install latest Hugo
run: |
TAG=$(curl -s https://api.github.com/repos/gohugoio/hugo/releases/latest \
| grep '"tag_name":' \
| head -1 \
| sed -E 's/.*"([^"]+)".*/\1/')
curl -L "https://github.com/gohugoio/hugo/releases/download/${TAG}/hugo_${TAG#v}_Linux-64bit.tar.gz" \
-o hugo.tar.gz
tar -xzf hugo.tar.gz hugo
sudo mv hugo /usr/local/bin/
hugo version
- name: Install rclone
run: |
curl https://rclone.org/install.sh | sudo bash
- name: Build site with Hugo
run: hugo --minify --enableGitInfo
- name: Configure rclone for Cloudflare R2
env:
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
run: |
rclone config create r2 s3 \
provider Cloudflare \
access_key_id "${R2_ACCESS_KEY_ID}" \
secret_access_key "${R2_SECRET_ACCESS_KEY}" \
endpoint "${R2_ENDPOINT}" \
--quiet
- name: Deploy to Cloudflare R2
env:
R2_BUCKET: ${{ secrets.R2_BUCKET }}
run: |
rclone copy public/ r2:"${R2_BUCKET}" \
--checksum \
--no-traverse \
--verbose
Rules
Go to the dashboard of the domain iany.me and go to the section Rules.
Redirect Rules
I have added 3 redirect rules:
- When incoming requests have a hostname
iany.meorwww.iany.me, redirect toconcat("https://blog.iany.me", http.request.uri.path). - Redirect
https://blog.iany.me/*/index.htmltohttps://blog.iany.me/${1}/. - Redirect
http://*tohttps://${1}
Although the Redirect Rules manual says if multiple rules make the same modification, the last executed rule wins, I have to put rule 2 before 3.
URL Rewrite Rules
I added 2 rewrite rules for index.html:
- When the request URL is
https://blog.iany.meorhttps://blog.iany.me/, rewrite the URL path to/index.html. - When the request URL matches
https://blog.iany.me/*/, rewrite the path to${1}/index.html
Cache Rules
I added 2 cache rules for images and static content:
- Set cache TTL to 1 month for
png,jpg,jpeg, andsvgfiles. - Set cache TTL to 1 year for files in
/js,/fonts,/css, and/uploads.