Returning statusCode in HTTP headers in BrowserRouter

I’m handling 404 requests with the following Route:
<Route path='*' element={<h1>404</h1>} /> is returning a 200 response to my browser.
How to specifiy that this Route will trigger a 404 statusCode response?

Check Meteor SSR to send the actual status code. Then check how to use React’s StaticRouter to use staticContext to pass the status code from your components to Meteor SSR functions.

2 Likes

Please check this answer: seo - 404 page needs meta description (just to make Google happier)? - Webmasters Stack Exchange

In React I use React Helmet to update my meta tags in the HTML header. You can also set this manually with effects.
My “Not found page”, set this tags into the page header:

      <title>Page not found</title>
      <meta name='errorpage' content='true' />
      <meta name='errortype' content='404 - Not Found' />
      <meta name='prerender-status-code' content='404' /> // in case you use prerender.

Header status code must come from the server. It cannot be generated by client code alone unless you use another tool (like prerender) to generate the correct header status code for you. React is not enough to handle this

I also just remember why this is confusing to some:

HTTP headers are different from HTML <head>

This is also explained in the link I copied above. However soft 404 works for many (including myself). It is easy to do it, if this works for the one who asked, that’s a really cheap solution.

The answer you linked actually highlights the importance of the correct http header of 404 not found (not html header nor soft 404).

If you are talking about soft 404:

  1. The OP above will already result to soft 404. There is no need for any meta for this to happen.
  2. Soft 404 is an error in Google Search console. In the linked question, it already mentioned that:
    2a. Returning 200 in an error page is a bad practice
    2b. Fix soft 404 errors

Ok, I understand your point, an HTTP 404 is recommended. For us a HTML 404 works fine because we prerended everything and basically … there is no server to return a HTTP 404 but the Prerender does that based on the HTML metas.

For SSR, I think this answers the question: Make server respond with 404 status on unmatched URLs - #4 by coagmano

2 Likes

as rjdavid said, you should opt in for SSR, there’s a small tutorial on meteor official documentation.
Then pass a context to your app :

//  server side
onPageLoad(sink => {
   const context = { error: null }
   const render = <App context={context} />
   if (context.error) {
     sink.setStatusCode(context.error)
   }
   sink.renderIntoElementById('app', render)
})

// in your react Error component
<Route path='*' element={() => {
   context.error = 404
   return (<h1>404</h1>)
}} />
1 Like

When using () => in element this error occurs:

Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
Routes@/node_modules/react-router/index.js:895:7
Router@/node_modules/react-router/index.js:833:7
BrowserRouter@/node_modules/react-router-dom/index.js:86:7

When using documentation example

onPageLoad(async sink => {
  const App = (await import("/client/App.jsx")).default;
  ReactDOM.hydrate(
    <App />,
    document.getElementById("membrane")
  );

The site is rendered but there latency where the component is renderer as [object Object] before being rendered.

When using renderToString() the error 'document is not defined" occurs

onPageLoad(sink => {
  const context = { error: null }
  sink.renderIntoElementById("membrane",  
  renderToString(<App context={context} location={sink.request.url} />)
);
})

Finally, to have something working, I kept my basic render (no hydrate) in client without the onLoadPage on client side.
And i used this onLoadPage in server side, to check if route existed, and if not setStatusCode to 404; (<App /> returns <BrowserRouter><Routes><Route> structure.

import App from "../src/ui/App.jsx";
onPageLoad(sink => {
  let path = sink.request.url.pathname;
  const routes = App().props.children.props.children.map(route => route.props.path)
  if (!path.endsWith('/')) { path = path + '/' }
  if (!routes.includes(path)) {
    sink.setStatusCode(404);
  }
})

The site is rendered but there latency where the component is renderer as [object Object] before being rendered is linked to Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.

This is because one of your components is not returning JSX but a function or an object. Check that all your components are returning JSX.
Also if your App is using async functions to render content (such as API / graphql calls) you should await on the server that the component was fully loaded (using a Promise or async / await)

When using renderToString() the error 'document is not defined" occurs

this is because document is a DOM object (so it’s not available on the server).
Somewhere in your code you have a call that uses the document object.
Wrap this call in a React.useEffect hook to avoid this error (and also all other DOM API calls).

Without more details within your app, we won’t be able to help you further.