Preface: Why Use Cloudflare R2 for Hugo Image Hosting?
When running a static site built with Hugo, image management is always a hot topic. The traditional approach is to dump image files directly into the project’s static/ directory and commit them to Git version control along with the code. However, as the number of images grows, this method can cause your Git repository to become ridiculously bloated. This not only slows down git clone and pull operations but also makes backups and management a real headache.
To tackle this problem, I turned my attention to Cloudflare R2. R2 is an object storage service compatible with Amazon S3, but its biggest superpower is its “zero egress fees.” This means that no matter how many users view your images, you won’t pay a dime for traffic. Paired with Cloudflare Workers, we can even unlock more advanced image processing capabilities.
This tutorial will walk you through, step-by-step, how to leverage Cloudflare R2 and Workers to build an efficient, stable, and “free” image hosting system for your Hugo website.
Implementation Steps: From Worker to Shortcode
The core workflow of this system is as follows:
Hugo Shortcode -> Cloudflare Worker -> Cloudflare R2
When a user browses your article, the shortcode in the post will send a request to the Cloudflare Worker. The Worker then fetches the corresponding image from R2 storage and returns it to the user.
Step 1: Create a Cloudflare Worker
First, you need to create a new Worker in your Cloudflare dashboard. This Worker acts as an intermediary, responsible for receiving image requests from your website and retrieving the images from R2.
Crucial Setting: In the Worker’s settings page, you absolutely must bind your R2 bucket to the Worker. This gives the Worker the necessary permissions to read files from your R2 storage.
Here’s a sample code for the Worker:
export default {
async fetch(request, env) {
// 1. Parse the request URL to get the image file path (key)
const url = new URL(request.url);
const objectName = url.pathname.substring(1);
// 2. Get the image object from the bound R2 bucket
// R2_BLOG_IMG is the variable name you assigned to the R2 binding in the settings
let object = await env.R2_BLOG_IMG.get(objectName);
// If the image is not found, return a 404 error
if (object === null) {
return new Response("Image not found", { status: 404 });
}
// 3. Set the HTTP headers, especially Cache-Control
const headers = new Headers();
object.writeHttpMetadata(headers);
// Cache the image in the browser for one year to reduce unnecessary requests
headers.set('Cache-Control', 'public, max-age=31536000');
// 4. Return the image content
return new Response(object.body, {
headers: headers,
});
},
};
Step 2: Create a Hugo Shortcode
Next, in your Hugo project, create a file named r2image.html in the layouts/shortcodes/ directory. This shortcode will allow you to insert images stored on R2 into your Markdown articles with a much cleaner syntax.
{{/* Get the key and alt parameters passed to the shortcode */}}
{{ $imageKey := .Get "key" }}
{{ $altText := .Get "alt" }}
{{/* Construct the full image URL */}}
<img src="https://your-worker-url.workers.dev/{{ $imageKey }}" alt="{{ $altText }}" loading="lazy">
Don’t forget: Replace your-worker-url.workers.dev with the actual URL of your Worker.
Step 3: Using it in Your Posts
Once you’ve completed the setup, you can easily insert images in your Markdown posts using the following syntax:
{{< r2image key="your-image-folder/your-image.png" alt="A description of the image" >}}
For example:
This will display the cyber-pikachu.png image stored in the root of your R2 bucket.
Conclusion: Benefits and Future Prospects
This image hosting system, built with Cloudflare R2 and Workers, offers several fantastic benefits:
- Lightweight Git Repository: By separating image assets from your codebase, your project’s version control stays slim and trim, speeding up cloning and deployment.
- Convenient Image Management: You can directly upload, delete, or organize your image folders through the Cloudflare dashboard, which is incredibly intuitive.
- Zero-Cost High Performance: R2’s zero egress fees, combined with the generous free tier for Workers, makes this a virtually zero-cost solution for most personal blogs and small to medium-sized websites. Plus, thanks to Cloudflare’s global CDN, image loading speeds are top-notch.
- High Scalability: Since all requests go through the Worker, you can easily add more features in the future, such as:
- On-the-fly Image Resizing and Cropping: Dynamically output different image sizes based on request parameters.
- Format Conversion: Automatically convert images to more modern, efficient formats like WebP.
- Watermarking: Automatically add a copyright watermark to all your images.
All in all, this is a highly recommended solution for any Hugo user.