< Back to articles

How to host static React apps in the Google Storage Bucket behind Cloudflare CDN

During app development at Ackee, we create quite a lot of static websites using React. When using Google Storage Buckets behind Cloudflare’s CDN, you will probably run into a few problems with the React Router, limited Bucket’s webserver, and https settings. Cloudflare also needs custom settings to serve the React app properly.

You’d usually use classic web-hosting from a local vendor with ftp-access-only for testing, staging, and producing. With ftp, hosting things might get a little tricky when you need to test or deploy something fast, separate multiple projects into multiple independent containers, or keep backups and versions. Also, migration and deployment to ftp hosted at your local vendor can be painfully slow.

Google Cloud

We deploy most of our stuff to our Kubernetes cluster hosted on the Google Container Engine. Having a small static website running as a nginx pod however, would be an overkill.

Static websites can be hosted in the Google Storage Bucket. Google Storage offers different types of Buckets such as Nearline (cheaper and redundant in one region), which is suitable for testing or staging an environment, and Multi-Regional (more expensive and redundant in multiple regions). With Buckets, we are able to keep “our stuff” in the same cloud while still being able to easily migrate and scale, saving us both time and money. We don’t recommend using them as a production hosting solution for the general public (keep reading to find out why).

google storage bucket use case
Bucket Storage types with region selection and domain name.

The Bucket's webserver

What are we waiting for? Let’s push our app to the Bucket and we are done, right?! Not so fast…

Imagine having the following code in your routes.jsx

<Route component={App}>            
  <Route path="/" component={AuthenticationFirewall(AccessSecurity(MainPanel), Login)}>
     <IndexRoute component={Homepage} />
     <Route path="/companies" component={Companies}>
       <IndexRoute component={CompaniesTable} />  
       <Route path="/companies/:id" component={CompaniesDetail} />
     </Route> 
  </Route> 
</Route>                 

Typically, you’d host your React app in a webserver using similar configuration (nginx syntax).

...  
  # Always serve index.html for any request  
  location / {  
    root /var/www/;  
    try_files $uri /index.html;  
   }  
...

So that all of the routes like /companies/125 are served by index.html and the webserver is not looking for a directory called companies and then for a subdirectory companies/125 (neither of them exist, they are just dynamic routes generated with javascript).

However, the GS Bucket’s webserver only lets us configure the file permissions, the index, and the 404 page. There are no .conf files, no .htaccess, nothing like that. With a deployed app in a fresh new bucket, accessing the /companies/125 page directly would immediately throw a 404 error.

We cannot modify the virtual host, but we can tell the webserver to serve index.html for any request with a simple trick of setting index.html as a 404 (not found) page. What this actually means is that if you cannot find the file specified by the URL, return /index.html which will do exactly the same thing as the nginx configuration above.

react in google storage bucket
How to serve index.html for any request in the Google Storage Bucket
# create bucket  
gsutil mb gs://example.ack.ee  
# rsync bucket with buildDir local directory, exclude excludeDir  
gsutil -m rsync -R -x ./excludeDir ./buildDir gs://example.ack.ee  
# allow everyone to see your website  
gsutil -m acl ch -R -u AllUsers:R gs://example.ack.ee  
# set the webserver index and error page  
gsutil -m web set -m index.html -e index.html gs://example.ack.ee

Cloudflare settings

Google Storage Buckets don’t support https. You can still achieve having the website running under https on the client’s side with a Content Delivery Network provider like Cloudflare.

Cloudflare has it’s own Error pages called Smart Errors. When you request a non-existing page that has the Cloudflare’s CDN enabled, instead of serving you the page’s 404 error, Cloudflare serves their default 404 page with some info about possible misconfiguration. This is a default behavior and it doesn’t work with our custom 404 page in our Google Storage Bucket.

To disable this “Smart Errors” feature, simply add a Page Rule with Smart Errors set to Off. For testing a domain, you can also turn off the cache for testing as seen in this image:

google cloud storage - cloudflare smart errors page rule
Turning off the “Smart errors” Cloudflare feature lets the Bucket’s webserver handle 404 errors correctly

You can easily set this Page rule using Cloudflare’s well-documented API in your CI/CD pipeline like we do using  Jenkins Pipeline plugin.

Conclusion

Although deployments and migrations of React apps (and other apps that need a more customizable webserver) are fast and comfortable with Buckets, we don’t recommend using them for production websites. The drawback of this handle-404-with-index.html is of course the returned 404 HTTP code in the header, which can mess up the SEO pretty badly. You could possibly rewrite the app router code to enforce the http header code to 200 when it renders the proper page. Or – use it for the continuous deployment of administration CMS or anything similar that is not meant for the general public.

Links

https://github.com/react-boilerplate/react-boilerplate/blob/master/app/.nginx.conf
https://cloud.google.com/storage/docs/hosting-static-website

Did you find this article helpful? Learn more about our app development!

Marek Bartík
Marek Bartík

Are you interested in working together? Let’s discuss it in person!