How to Prerender React Apps Using Prerender and NGINX











The prerender service will cache the static HTML for a while before requesting a new copy from the server. This method did not require me to change anything on my React app and only required me to make some adjustments to the NGINX server configuration.

This is how my application was being served before prerender came into the mix:















After adding prerender, the flow looked something like this:














Setting up prerender service


There are two ways you can make use of the prerender service. You can either make use of the paid hosted service (contains a free plan as well) or you can run the open source service on your server and interface with that. I decided to opt for the latter. The first step was to install prerender on the server. It was fairly straightforward to do so using npm:

$ npm install prerender

The next step was to create a new node script that ran the service. These 3 lines are enough to get the service up and running:

const prerender = require('prerender');
const server = prerender();
server.start();

Save this code to a server.js file and run it using node:

$ node server.js

At this point you can go ahead and test whether the prerender service is working correctly or not by opening http://localhost:3000/render/?url=https://google.com/. This should display the Google homepage. If the images don’t show up correctly, don’t worry. The issue will be fixed when we serve the prerender service via NGINX.

Run prerender on system start


The next step is to run prerender on system start and make sure it keeps running in case of crash or system restart. We will make this happen by creating a systemd service file. I am assuming that you saved the server.js file in your home folder. My home folder is technopundit but yours might be different so make sure you edit the WorkingDirectory path. Create a prerender.service file in /etc/systemd/system folder with the following contents:

[Unit]
Description=Prerender server for bot crawling
After=network.target

[Service]
User=technopundit
WorkingDirectory=/home/technopundit/
ExecStart=/usr/bin/node server.js
Restart=always

[Install]
WantedBy=multi-user.target

In this file, we are telling systemd to start server.js when the network is up and running. The service would launch under the user technopundit and the command it needs to run is /usr/bin/node server.js. The script would also automatically restart in case of a crash or system restart.

After saving this file, let’s make sure systemd can recognize it:

$ sudo service prerender status

● prerender.service - Prerender server for bot crawling
     Loaded: loaded (/etc/systemd/system/prerender.service; disabled; vendor preset: enabled)
     Active: inactive (dead)

Perfect! Now let’s start the service and then check the status:

$ sudo service prerender start
$ sudo service prerender status

● prerender.service - Prerender server for bot crawling
     Loaded: loaded (/etc/systemd/system/prerender.service; disabled; vendor preset: enabled)
     Active: active (running) since Sat 2022-08-27 19:59:46 UTC; 2s ago
   Main PID: 589168 (node)
      Tasks: 7 (limit: 1137)
     Memory: 41.8M
     CGroup: /system.slice/prerender.service
             └─589168 /usr/bin/node server.js

Aug 27 19:59:46 systemd[1]: Started Prerender server for bot crawling.

Integrating prerender with NGINX


Now that our prerender service is running, we can go ahead and integrate it with NGINX. What we want to do is that the normal user should be sent the normal HTML + JS response but a bot should be sent a response by the prerender service.

The original NGINX configuration file for my React app looked like this:

server {
    server_name example.com;
    root /home/technopundit/example/build;
    index index.html;

    location / {
  	    try_files $uri /index.html;
        add_header Cache-Control "no-cache";
    }

    location /static {
        expires 1y;
        add_header Cache-Control "public";
    }

    location /api {
        include proxy_params;
        proxy_pass http://localhost:5000;
    }
}

This is a fairly generic configuration file. We add some cache headers to certain path responses and pass the /api route traffic to the gunicorn server running on port 5000. Now we just need to make sure that all requests made by a bot are responded to by the prerender service that is running on port 3000. The template file for these changes is conveniently provided by the prerender folks. I took the same file and tweaked it a bit to make it work for my setup. The major thing I changed in the template was to edit the prerender service URL and remove the proxy header part. As I am using a self-hosted service, I replaced service.prerender.io with 127.0.0.1:3000 and because this is our service, we don’t need to pass any authentication headers.

The resulting NGINX configuration file looks like this:

server {
    server_name technopundit.blogspot.com;
    root /home/technopundit/rxcode/REACT/build;
    index index.html;

    location / {
  	    try_files $uri @prerender;
        add_header Cache-Control "no-cache";
    }

    location /static {
        expires 1y;
        add_header Cache-Control "public";
    }

    location /api {
        include proxy_params;
        proxy_pass http://localhost:5000;
    }

    location @prerender {
        set $prerender 0;
        if ($http_user_agent ~* "googlebot|bingbot|yandex|baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest\/0\.|pinterestbot|slackbot|vkShare|W3C_Validator|whatsapp") {
            set $prerender 1;
        }
        if ($args ~ "_escaped_fragment_") {
            set $prerender 1;
        }
        if ($http_user_agent ~ "Prerender") {
            set $prerender 0;
        }
        if ($uri ~* "\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)") {
            set $prerender 0;
        }

        if ($prerender = 1) {
            set $prerender "127.0.0.1:3000";
            rewrite .* /$scheme://$host$request_uri? break;
            proxy_pass http://$prerender;
        }
        if ($prerender = 0) {
            rewrite .* /index.html break;
        }
    }
}

I removed the SSL support and redirection from this configuration file for the sake of simplicity.

Testing NGINX configuration


In order to test whether our NGINX config changes did not break anything, we can run:

$ sudo nginx -t

If everything seems correct, we can restart NGINX:

$ sudo service nginx restart

To test whether our service is working the way it is supposed to, we can run the following CURL command:

$ curl -A googlebot https://example.com

Replace example.com with your React-based app URL and see if the output of this command is different from if you run curl without -A googlebot.

If you have reached this step then chances are that your prerender service is working fine. In case there are errors, please write about them in the comments below and I will try to help.




Comments

Popular posts from this blog

How to download a file using command prompt (cmd) Windows?

Angular 9 - User Registration and Login Example & Tutorial

How to Include ThreeJs in Your Projects