React / Angular App with Spring Boot Backend – How to Fix BrowserRoute URLs in Production for SEO

Update: 12/20/2020
After I converted all my HashRoute URL to BrowserRoute URL, and did some mod rewrite in tomcat, Google has successfully crawled my website and indexed it.

So you got your react app running locally (and probably angular too) with the backend service or REST API running on Spring Boot with Embedded Tomcat.

Then one day, you deployed your code to production that had external tomcat. The app home page came up but the URL links to your other react routes don’t seem to work !! If so you have landed on the right page to understand the problem and fix it.

First of all let’s touch base little bit on the Reac Routes. The ReactJS mainly provides two major kinds of Routes for your Components. These routes make your URLs looks slightly different.

Let’s say you have a site called http://www.nepalflow.com and your UI is running at the root context.

If you used a HashRouter, your URLs to other Routes (or pages) will looks something like

http://www.nepalflow.com/#/ui/contact_us

However, if you used a BrowserRouter, then your url fo the same Route would look like.

http://www.nepalflow.com/ui/contact_us

Rendering the HashRouter should not be much of a problem because the navigation is controlled by the JavaScript solely in the Browser itself.

However, the BrowserRouter will hit the backend server (also is the case if the site is bookmarked). Now suddenly, you might not have that Request Mapping on the API or the backend side, since you were not serving the UI from the backend and the Spring Boot or other frameworks will throw 404 error.

Well, at least that’s not how you intended your app to look like, right?

So here are a couple of solutions, I have done for my project to fix this issue of broken BrowserRouter links for React and I am pretty sure you can follow similar concept if you are working in an Angular App also.

SOLUTION FOR EMBEDDED TOMCAT CONTAINER:
Tomcat 8+ provides something called Valve, the equivalent of apache mode_rewrite. So in order for the backend to handle the UI url (which you need to forward to the SPA index.html), you can define a rewrite.config file.

Here is a sample rewrite.config file which I am dropping to the /resources folder. Inside this config file, all I am doing is forwarding certain URLs to index.html, in this case, anything that starts with /ui right after domain name. It is important to write this rule properly because you would still want the backend to serve other APIs. for that reason, I have all my backend APIs with the context /api/, which will be untouched by the /ui/ rules below.

RewriteRule ^/ui/(.*)$ /index.html [NC]

This rule is simply saying, if any request comes with a /ui/, send it the index.html and then the ReactJs or Angular will take care of rendering the proper component based on the route.

But simply having that rule is not enough. You need to tell the spring boot to configure the embedded container based on that file. So here is a little bit of code that I got from StackOverflow and added a few features, specially the Profile annotation to use it only for local embedded container. This is because the container is provided for me in production and I am using a different solution (described later in this article) for production.

Now when I start the appliation in embedded tomcat container (not the React’s 3000 port), I am getting the proper URLs rendered because of this rewrite feature of the container.

SOLUTION OR FOR PROVIDED CONTAINER:
As I mentioned above, I have an external tomcat in production, so this solution did not work. However, if you have an embedded tomcat container in production, it should work.

So for the provided tomat container, I had to go some extra miles. The idea is the same, you just rewrite your urls to forward the ui routes to React or Angular’s SPA index.html. However, the where to make this configuration change is what you probably need to know.

First of all I had to enable the Valve in my tomcat. For that I had to add the following to the server.xml (inside Host node) and in the context.xml just before the ending </context>

 <Valve className="org.apache.catalina.valves.rewrite.RewriteValve" />

The configuration above will enable Valve or mode rewrite in your tomcat. However, it needs to know the rewrite rules. After making the changes above, I had to drop the rewrite.config file with the following rule to /WEB-INF folder for the unarchive WAR file.

RewriteRule ^/ui/(.*)$ /index.html [NC]

Hope this helped some of you !! Have a good day.

How to copy React JS build files to Springboot Resources folder.

Unlike angular, where you could specify your build output, reacjs does not seem to have a way to do that (to my knowledge). So I had to write a simple shell script to do the copy after the build is done.

My ReactJS project is located under: src/main/webcontent
So the build files are located under: src/main/webcontent/build

However, the static resources spring boot uses are located under src/main/resources

Here is the resources folder:

So in order for me to copy these files after the build is done, I wrote a very simple shell script. The script changes directory to webapp folder, deletes any pre-existing files by iterarting through sub folders. It then iterates and delets the subfolders themselves. Finally, it changes directory over to the folder where the built ReactJS files are located. It they issues a xCopy command to copy them over to the static folder.

REM COPYING FILES FROM REACJS BUILD FOLDER TO RESOURCES FOLDER
cd src\main\resources\webapp
del /S /F /Q *.*
RMDIR static /Q /S
cd ..\..\webcontent\build
xcopy *.* ..\..\resources\webapp\ /s /i

cd ..\..\..\..

I put this script on a .bat file along side pom.xml. So that I can just quickly issue the command after the maven build is done (which includes auto build of reactjs). So my sequence would be:

>mvn clean package
>publish_ui.bat