Suppose you want to deploy multiple Gatsby.js sites using the following setup.

Then you have to configure NGINX like this:

server {
    listen             ssl http2;
    server_name       ;

    ssl                         on;
    ssl_certificate            /usr/local/etc/dehydrated/certs/;
    ssl_certificate_key        /usr/local/etc/dehydrated/certs/;

    # Gatsby static pages

    location / {
        rewrite                 ^/(.*)$ /gatsby-site-00/$1 break;
        proxy_pass    ;


If you open the site in a web browser and click on a link which points to the events page react-router will set the URL to Note the missing trailing slash (.../events instead of .../events/) ;-) Also remember that sites generated with Gatsby.js are single-page applications (SPAs).

The missing trailing slash in generated URLs with regard to Gatsby.js and react-router has also been discussed here.

Now suppose a user bookmarks the URL If the user wants to open this bookmark in a new browser window the Apache server will respond with a HTTP 301 (the Location header field has already been modified by NGINX):

zaunerc@2217PC12387:~/repos/keys$ curl -I
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Wed, 20 Jun 2018 17:28:35 GMT
Content-Type: text/html; charset=iso-8859-1
Connection: keep-alive


Why? Because there exists no file named events. But there exists a directory named events. And to address a directory on a web server you should include a trailing slash.

And that’s why we have to include the proxy_redirect directive (docs). This sets the text that should be changed in the “Location” and “Refresh” header fields of a proxied server response.

By the way. Apache still delivers a default 301 status page which content is not modified by NGINX. But that doesn’t matter. Web browsers only use the information provided by the HTTP response headers:

zaunerc@2217PC12387:~/repos/keys$ curl
<title>301 Moved Permanently</title>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="">here</a>.</p>