At Ackee, we develop 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

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

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

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

4 KOMENTÁŘE:

  1. This is all nice but it would be nice to automate this process. Ideally, I would like to make a build whenever I push to say my develop branch.

  2. Hi Marek,
    Do you know how to set up for “Fetch as Google”?

    When I follow your instruction, everything works fine. I can see every pages from my browser. But not “Fetch as Google”.
    “Fetch as Google” is one of tools in google search console. It allows us to check google indexing. If I put my React static app on GCP storage, “Fetch as Google” recognises only top root. Other than that, like /companies/125 page is not recognised and return “Not found”.

    Do you know how to solve this problem?

    1. Yep, that seems right. It is the limitation of this proposed solution that I mentioned – Google Storage Bucket webserver return a 404 and index.html which handles the request for /companies/125 and returns a proper web content BUT the http error code 404 is still present.
      That is why this solution is not suitable for something where you care about SEO but rather for small internal CMS and simple microsites.

Leave a Reply

Your email address will not be published. Required fields are marked *