For most of my bigger projects, I like setting up staging websites so I can let other users try out the website or to see how the website runs on a webserver myself. Up until now, these staging websites were accessed over unprotected HTTP to disable Cloudflare caching and had basic authentication to prevent unwanted visitors. It was time for a better solution.

Bypassing the Cloudflare cache#

A great feature by Cloudflare is that all assets like CSS and JavaScript files are cached by their server. However, for testing, this can be bothersome if you update the website a lot. So, I want to disable the Cloudflare cache without losing the https encryption.

Currently, I have a rule that depicts that all domains matching *.deviaene.eu/* should go through the https proxy, and all https requests have to use the asset cache. Because there is no regex support for page rules, it is not possible to change this rule to have special rules for a specific subdomain. However, we can add another rule for each of the subdomains. To try this out, I created a new rule after this one that tells Cloudflare to run stage.deviaene.eu through the proxy as well, but set the cache policy to "ByPass".

Cloudflare page rules

After doing this, you should also update the DNS record to pass the requests through the Cloudflare proxy. After a few seconds, the updates already took effect. The website now went over https and records were not cached. You can check if Cloudflare caches requests by looking at the response headers. If your request is cached it will have a cf-cache-status: HIT header.

Restricting access#

Because we set up the Cloudflare proxy for this website, we can no longer use the IP address from the request for access management. This IP will now be the IP address from the closest Cloudflare server. Luckily NGINX has a solution for this.

Prevent 3rd party access

First thing you should do is blacklist all non-Cloudflare IP addresses. This will prevent the website from being visited through any other means but the Cloudflare proxy. All Cloudflare IP ranges can be found on their public website, so this can easily be configured with NGINX.

I would recommend making a cloudflare-whitelist.conf file including the Cloudflare IP ranges so you don't have to do this for all your websites.

# cloudflare-whitelist.conf

allow 199.27.128.0/21;
allow 173.245.48.0/20;
...
deny all;

Restrict access by IP address

Now that we have restricted the direct access to Cloudflare servers only, we need a way to limit access by the visitors IP address. Fortunately for us, Cloudflare sends the original visitors IP address to the server as well in the form of a header named CF-Connecting-IP.

NGINX has a module called ngx_http_realip_module to help us with this. First off, we will have to change the last code snippet lightly. A new directive, set_real_ip_from, tells NGINX to set the "from IP" to another value for certain IP addresses. Then, we can use real_ip_header to tell NGINX to use a certain header as the IP address from that moment. Now we can use the allow and deny directives with actual IP ranges and set the access rights accordingly.

# ip-blacklist.conf

set_real_ip_from 199.27.128.0/21;
set_real_ip_from 173.245.48.0/20;
...

real_ip_header CF-Connecting-IP;

allow xxx.xxx.xxx.xxx;      # Replace with real ip ranges
allow yyy.yyy.yyy.yyy
deny all;

We are secure once more!#

At this point, we have secured the website entirely using https so all traffic is encrypted. We also added restrictions to what IP addresses can access the website. I would recommend to anyone using public staging websites to use at least a part of what I did here to make sure no unknown sources will reach these websites.

Since I implemented all of these settings, the amount of unwanted visitors on my staging websites has been reduced to zero. :)