Search code examples
nginxwebserverdigital-oceanhttp-status-code-301

Gatsby with nginx - redirect is broken - trailing / (slash)


My gatsby site redirects to trailing slash and then redirects again back to non-trailing slash.

nginx config file:

server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    include snippets/ssl-example.com.conf;
    include snippets/ssl-params.conf;

    server_name example.com;
    return 301 https://www.$server_name$request_uri;
}

server {
      listen 443 ssl http2;
        listen [::]:443 ssl http2;
        include snippets/ssl-example.com.conf;
        include snippets/ssl-params.conf;

        root /var/www/example.com/html;

        index index.html index.htm index.nginx-debian.html;

        server_name www.example.com;

        location ~ /.well-known {
                allow all;
        }

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }

        location ~* \.(?:html)$ {
          add_header Cache-Control "public, max-age=0, must-revalidate";
        }

        location = /sw.js {
          add_header Cache-Control "public, max-age=0, must-revalidate";
        }

        location /page-data {
          add_header Cache-Control "public, max-age=0, must-revalidate";
        }

        location /static {
          add_header Cache-Control "public, max-age=31536000, immutable";
        }

        location ~* \.(?:js|css)$ {
          add_header Cache-Control "public, max-age=31536000, immutable";
        }

    error_page  404  /404.html;
}

When I open my website with a trailing slash, I see a redirect to the same URL but without a trailing slash:

example.com/hello-world/ -> example.com/hello-world

That's the expected behavior.

But when I open my website without a trailing slash, I see a redirect to a trailing slash, and then a redirect to without a trailing slash again.

example.com/hello-world -> example.com/hello-world/ -> example.com/hello-world

This should not happen. Where did I go wrong in my configuration? Any help is very much appreciated.


Solution

  • nginx

    Nginx's behaviour regarding slashes is documented here.

    If a location is defined by a prefix string that ends with the slash character, and requests are processed by one of proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass, or grpc_pass, then the special processing is performed. In response to a request with URI equal to this string, but without the trailing slash, a permanent redirect with the code 301 will be returned to the requested URI with the slash appended. If this is not desired, an exact match of the URI and location could be defined like this:

    location /user/ {
        proxy_pass http://user.example.com; 
    }
    
    location = /user {
        proxy_pass http://login.example.com; 
    }
    

    This doesn't apply to you because you are not using CGI or upstream app server...you are just serving files (if I understand correctly).

    I'm not a gatsby expert but from the link in your comment...

    Gatsby works better, smoother, cleaner (etc.) with trailing slashes

    There are a variety of nginx configs floating around from people that have used them successful with Gatsby. Here is one for instance.

    The most relevant parts for you are...

    redirect paths that don't contain . or ? AND don't end with / to the same path but with trailing /.

    e.g. redirect /foo/bar to /foo/bar/ but don't mess with /foo/bar.ext or /fo.o/bar or /foo/bar?hello.

    rewrite ^([^.\?]*[^/])$ $1/ permanent
    

    try to serve the requested URI interpreting it in the following order of preference:

    • as is (path to file)
    • as a directory
    • as directory containing index.html file
    • finally 404 error if none of the above were valid
    try_files $uri $uri/ $uri/index.html =404;
    

    gatsby

    From the gatsby issue link you posted they say the following:

    Explicitly use the trailing slash in all usages of the <Link> component and/or invocations of navigate()

    and

    Install gatsby-plugin-force-trailing-slashes. Even though Gatsby generates the static html pages within their own named directories by default, this plugin forces the path value for each page to end in a / - critical for configuring the core Gatsby @reach/router at build time.

    and then finally rebuild the gatsby site since some of this config only has impact at build time.