Hey guys, I hope this woks!
With the recent announcement of the new free tier for Cloudflare Workers, I found no more excuses not to use the full depth of my copy & paste JavaScript programming skills. (Thank you, Cloudflare Workers Recipes authors!)
One of the big problems for Cloudflare users with sites on shared hosting is that hackers may find the IP address and connect directly to it, bypassing Cloudflare and the protections it offers. So hereâs a solution involving a Cloudflare worker, a few .htaccess directives, and a Firewall Rule.
With a very simple Cloudflare Worker, we can add a request header, a header that will be sent from the edge (any of Cloudflareâs 180+ data centers) to the origin (your server), and therefore wonât be visible to site visitors. As long as the header name and value are kept secret by the site admin, any requests not coming through Cloudflare will not have this header, and will therefore trigger a rewrite condition at the origin server, and be redirected back to, well, Cloudflare - where a Firewall Rule will block it.
The worker (taken from this recipe):
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
/**
* Send header to origin, allowing for
* .htaccess to block requests
* not coming from Cloudflare
*/
async function handleRequest(request) {
// Make the headers mutable by re-constructing the Request.
request = new Request(request)
request.headers.set('Secret-Header', 'SeCrEt-kEy')
return await fetch(request)
}
The .htaccess directives (place them at the top of the file):
# Route visitors not coming from Cloudflare to, well, Cloudflare
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Both the header and the value should be kept secret
RewriteCond "%{HTTP:Secret-Header}" "!SeCrEt-kEy"
# Uncomment and edit w/ IP of services such as certs, cron, Softaculous etc
# RewriteCond "%{REMOTE_HOST}" "!^xxx\.xxx\.xxx\.xxx$"
RewriteRule .* "accessdenied.php" [R,L]
</IfModule>
What these directives do is check every request to see if it has a request header named âSecret-Headerâ and whether its value does not contain the string âSeCrEt-kEyâ. If the header does not exist, or does not contain the key, the request will be redirected to a non-existing URI named here âaccessdenied.phpâ, (a fictitious, non-existing file) which must be added in a Firewall Rule:
(http.request.uri.path eq "/accessdenied.php") Then... Block
Iâve seen a similar solution, that tested for the existence of any CF-*
header, mentioned a few times in this Community, but a hacker can always add a bunch of phony CF-*
headers to the requests performed by their bots.
Also, I prefer to redirect to Cloudflare instead of blocking these requests at the origin because a redirect takes ~200 bytes, while a standard 403 response page from my origin will take about 6x that amount. I could rewrite the 403 page down to a few bytes, but I still want to keep my site functional for legit visitors, and that includes meaningful error pages whenever they face one.
One consequence of this approach is that if you monitor your server logs, youâll find it may contain both legit 302s as well as, should anyone ever try to access your website via IP address, 302s for the URLs the bot was trying to reach. The Cloudflare Firewall Events log will then have the entries for the blocks executed for URL "/accessdenied.php"
.
After implementing this solution, you may find that there are 302s on your server log that have no match on the Firewall Events log. This is because bots may be programmed not to follow redirects. Itâs important to note that for legit bots accessing directly your site to provide services such as cron jobs, cert renewal etc, thereâs the bypass rule on the .htaccess directives above, you just need to uncomment and edit it to include the serviceâs own IP address.
Cloudflare Workers is a paid service, and costs $5/month for the first 10 million requests, $0.50 per up to 1 million additional requests. (While Iâve tested this setup using the âfree tierâ, it isnât advisable to use the free tier on a production website, as it has some limits that, when reached, will make the site break and generate 1015/1025 error pages for the duration of the rate limiting period, something about which site owners have no control.)
I canât say this hack is going to work for everybody. Iâve tested it many, many times in the past few days (what a royal pain to test htaccess!), but since every host has different configs, firewalls, caching etc and since I just had one platform to test it (shared hosting on Siteground, Apache 2.4.x), I hope you guys will be kind enough to share your feedback if something goes wrong.
Happy Workering!