Nginx + R2

This is a quick tutorial on setting up Nginx to proxy files from Cloudflare R2. All the files are available on GitHub. This tutorial was done with docker for easy setup but the config file is simple and easy to adapt to a full running nginx server.

Create Bucket

First thing that is needed is an R2 bucket with public access enabled (Enable Public Access Docs). I would recommend configuring a custom domain so that you can get additional benefits like WAF and Cache. Docs.

Upload images

After the bucket is created, you'll need to upload your images. This is how mine looks:

R2 Bucket Layout

Create nginx config

Here is a simple server config for nginx.

 1    server {
 2        listen 80;
 3        set $bucket <public bucket URL>;
 4        server_name <nginx server name>;
 6        location / {
 7            root /usr/share/nginx/html/;
 8        }
10        location ~ ^/image/(.*)$ {
11            resolver;
12            add_header             Cache-Control max-age=31536000;
13            proxy_ssl_server_name on;
14            proxy_set_header       Host $bucket;
15            proxy_pass https://$bucket/$1;
16        }
17    }

The gotcha setting here for me was proxy_ssl_server_name on; which is needed to pass the server name with SNI. This configuration removes the /image/ path when making the request to Cloudflare. It means that all our images can be stored in the root level of the bucket. I am also using the following filesfor this demo:


 1version: '3.9'
 4  web:
 5    image: nginx:mainline-alpine
 6    volumes:
 7      - ./nginx.conf:/etc/nginx/nginx.conf:ro
 8      - ./index.html:/usr/share/nginx/html/index.html:ro
 9    ports:
10      - 80:80

If you don't want to use docker compose then you can run it all with docker run --rm -p 80:80 -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro -v $(pwd)/index.html:/usr/share/nginx/html/index.html:ro nginx:mainline-alpine.


1<!DOCTYPE html>
3    <title> Nginx + R2</title>
6    <p>This page is served from nginx. The image below is served from R2</p>
7    <img src="/image/example.png" alt="example image from R2">

After starting docker, browse to localhost on your local machine to see the image be loaded from R2.