GatsbyJs client only paths goes to 404 page when the url is directly accessed in browser in "production"
Asked Answered
T

11

34

I have created a Gatsby app and configured gatsby-node.js to a create client only paths, which are all working fine in development while directly accessing the url of the path but not in production.

example :

if(page.path.match(/^\/sample/)){
     page.matchPath = "/sample/:value1/:value2/:value3";
     createPage(page)
  }

I am using heroku to deploy the app

Taxi answered 28/8, 2018 at 6:3 Comment(5)
Can you clarify what you mean by “client only paths” and what your specific problem is?Aryanize
client only path means, the route we specify with params in gatsby-node.js file. for example when we navigate, the route would be like this: "routeName/:param1/:param2/:param3"Taxi
Gatsby can't generate static pages that exist at paths that you don't provide. If you wanted to setup your server to route those requests to a page generated by Gatsby that uses parameters to internally route or change content you could do that, but Gatsby can't handle it solo.Aryanize
Thank you for your suggestion coreyward .Taxi
I have the exact same problem and am eager to understand the expected solution. Currently hosting with firebase.Coppins
C
59

The Why

While the client-side router knows about this path, there is no corresponding HTML file. When the browser looks at the site it first loads the 404.html file generated by gatsby, which includes the client-side router. Once the router completes its initialization it reads the path and loads the correct page. Meaning you end up at the right place but there's half a second of landing on the wrong page.

How to fix It

The general solution is to tell your server to redirect the /sample/ path to your /sample/index.htmlfile. The way to do this depends on your host, but I'll provide the name of the technique for various hosts in case you want to look it up. It's usually called URL Rewriting and should be supported by every major hosting platform.

Heroku

The Heroku section of the gatsby deploy documentation suggests using the heroku-buildpack-static module which has built-in support for "custom routes" which will solve this for your case using syntax like this:

{
  "routes": {
    "/sample/**": "sample/index.html",
  }
}

AWS Amplify

You need to add the redirect in the AWS Amplify console. For this example, the params are:

  • Source URI: /sample/<*>
  • Target URI: /sample/index.html
  • Type: 200
Coppins answered 12/10, 2018 at 0:14 Comment(7)
this works perfectly (key is specifying index.html). This should be the accepted answer.Curable
I tried with firebase hosting as you've suggested. But not, working. Can you elaborate and example for firebase, please?Endogen
You can't have 200 with CloudFlare. Only 301/302 forwarding is allowed. (The new CloudFlare Pages might have a solution for this, but I still haven't received the beta access, so I'm only guessing)Eusporangiate
Thanks, this got me on the right path to fixing this problem on Vercel with rewrites. Solution here: https://mcmap.net/q/435454/-gatsbyjs-client-only-paths-goes-to-404-page-when-the-url-is-directly-accessed-in-browser-in-quot-production-quotEversole
Works great, I had this issue again and found this answer and then realised I had liked it before, thanks again for sharing!Modigliani
having this issue locally when running gatsby. is this solution supposed to work on local node server(npm run dev/start) ?Dwanadwane
Hi, i'm using apache as webserver. My problem is that I've parameters in the url. How can I modify the configuration of Apache2 to redirect the parameters too? Otherwise the canonical url is always the same and it does not show the right parameters. Thanks a lotLubricity
F
4

This solves my problem

pages/404.jsx

import React, { useEffect, useState } from 'react';

export default ()=>{
  const [isMount, setMount] = useState(false);
  
  useEffect(() => {
        setMount(true);
  },[])

  if(!isMount) {
     return(
        <div>loading</div>
     )
  }

  return (
     <div>Page Not Found</div>
  )


}

This will not show a 404 error on the client-side render until the response is 404.

Frazier answered 22/2, 2021 at 9:27 Comment(0)
A
2

For anyone who uses S3 (gatsby-plugin-s3) and CloudFront, I've resolve 302/404 by adding generateMatchPathRewrites: false in the gatsby-plugin-s3 config and creating a Lambd@Edge function for origin request with code below:

exports.handler = async (event) => {
  const request = event.Records[0].cf.request;
  if (/^\/app\//i.test(request.uri)) {
      request.uri = '/app/index.html';
  }
  return request;
};

Though 302/404 went away, I still have the issue when doing a hard refresh. For example when I am at /app/page2 and hit refresh, the default component in /app will load then half a second later the component in /app/page2 will show up but in a weird way. Some css classes of /app is mixed in /app/page2. If anyone has any idea about this, please let me know.

Arapaima answered 9/5, 2020 at 10:5 Comment(1)
This is working for me. I am not seeing any styling issues at the moment. I now have my S3 bucket completely private! Only accessible from CloudFront and refreshes work on client only routes!Manic
E
2

Vercel

For anyone looking trying to solve this on Vercel platform, the answer is rewrites: https://vercel.com/docs/configuration#project/rewrites

If you have a gatsby app with client-only paths, as per something like https://www.gatsbyjs.com/plugins/gatsby-plugin-create-client-paths/, it's likely you will encounter this issue on deployment.

Example: your application has a path like mything.com/app/user-profile.

In this case, app/* are client-only routes that don't get generated on the server, so you may get a flash of a 404 when you reload this route or land on it directly from an external link, for example.

To solve, add a rewrite on the /app/* server side route in Vercel to point back to /app where, it is assumed, your routing is controlled by Reach router or similar in your pages/app.js file.

To set this up on Vercel, create a vercel.json in the root of your gatsby/react project and add the following configuration:

{
  "rewrites": [{ "source": "/app/:match*", "destination": "/app" }]
}

This issue will generally only manifest itself when deployed to production/staging. It won't occur in development (via gatsby develop anyway).

Eversole answered 9/2, 2021 at 14:8 Comment(0)
F
2

For local development, you have to use the magic [...].js filename.

You can take a look at the demo repo here: https://github.com/gatsbyjs/gatsby/tree/master/examples/client-only-paths/src/pages

Fitful answered 7/4, 2021 at 3:44 Comment(2)
which gatsby version was this introduced?Dwanadwane
This really saves my day! The name, '[...]' was easily misunderstood as a wildcard that I can rename as anything.Uncanny
M
1

I looked in the public folder to find the index file for the client route I created

for netlify I added the below to the netlify.toml file at the root.


[[redirects]]
  from = "/user/dashboard/"
  to = "/user/index.html"
  status = 200

Marashio answered 26/3, 2020 at 7:44 Comment(0)
A
1

For Firebase hosting, I added the following to my firebase.json:

"rewrites": [
  {
      "source": "**",
      "destination": "/loading/index.html"
  }

]

Then I created a new loading.js file under /page. With this configuration, Firebase first responds with loading.html then Gatsby handles the client routing.

Airflow answered 3/11, 2020 at 0:56 Comment(0)
L
1

NGINX

If your environment includes Nginx you should add something like this into your virtual host config:

rewrite ^/products/([0-9]+)$ /products/[id]/index.html;

or

rewrite ^/users/confirm/(.+)$ /users/confirm/[code]/index.html;

etc.

Just go to your production root (by default this is a /public directory) and look for the correct path to the index.html file for your case. For example: /public/products/[id]/index.html

Usually your virtual host config is located here: /etc/nginx/sites-enabled/mysite.com.conf

Lozier answered 4/5, 2021 at 10:31 Comment(0)
R
0

The heroku solution was not working for me, but I don't know exactly why. My frontend is fetching data from a express.js back end. I solved my problem by adding the following lines in my server.js to redirect my clients only routes.

app.get('/user/**', (req, res) => {
  res.sendFile(path.join(`${__dirname}/public/user/index.html`));
});
Raeleneraf answered 28/12, 2020 at 14:21 Comment(0)
O
0

For anyone using Gatsby Cloud, you add this to your gatsby-node.js

Working with Redirects and Rewrites

// gatsby-node.js

exports.createPages = async ({ actions: { createRedirect } }) => {
 
  ...

  createRedirect({
    fromPath: '/sample/*',
    toPath: '/sample/index.html
  });
};

Outnumber answered 30/8, 2022 at 21:11 Comment(0)
H
0

I have a Gatsby React application using client-only routes ([spanishBlogTitle].js), and the dynamic routes where blinking the Home page before rendering correctly.

FIX:

For AWS Amplify this is my JSON in Rewrites and redirects:

[
{
    "source": "/<*>",
    "target": "/index.html",
    "status": "404-200",
    "condition": null
},
{
    "source": "/blogs/es/<*>",
    "target": "/blogs/es/[spanishBlogTitle]/index.html",
    "status": "200",
    "condition": null
},
{
    "source": "/blogs/en/<*>",
    "target": "/blogs/en/[englishBlogTitle]/index.html",
    "status": "200",
    "condition": null
}

]

Hedonics answered 13/2 at 14:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.