Gatsby Private Routing not working as intended

Hi everyone,

I’m trying to implement a blog with gated content using Gatsby as the front-end and Strapi as the CMS for authentication. Have been following the instructions from the official Gatsby docs on client-only routes and user authentication.

I want to make the following URLs private:

  • /app/articles : This is the articles list page
  • app/articles/:slug: URL for individual article page

If an unauthenticated user visits the above 2 URLs, they should be redirected to /app/login page.

The issue I’m facing here is, the Private route seems to work only for /app/articles (the unauthenticated user gets redirected to /app/login), but not for app/articles/:slug - the page displays the complete article even when the user is unauthenticated.

All the pages under both the URLs are prebuilt when I run the gatsby-server so that I can take advantage of static-site performance. I want to just prevent a user from seeing the content if they aren’t logged in.

Can you please help me understand if I’m doing something wrong? Here is my code to get the complete picture.

File: /src/pages/app.js

const Index = ({location, history}) => {
                <PrivateRoute path="/app/articles/:slug" component={Article} />
                <PrivateRoute exact path="/app/articles" component={Home}/>
                <Login path="/app/login" location={location} history={history}/>
                <Signup path="/app/signup" />
export default Index;

Individual Article pages is being generated using gatsby createPages API inside gatsby-node.js. And the template resides inside /src/templates/article.js

File: gatsby-node.js

exports.onCreatePage = async ({ page, actions }) => {
    const { createPage } = actions
    if (page.path.match(/^\/app/)) {
        console.log("matched app", page.path.match(/^\/app/))
        page.matchPath = "/app/*"
        // Update the page.

exports.createPages = async function ({ actions, graphql }) {
    const { data } = await graphql(`
        query {
            allStrapiArticle {
                edges {
                    node {

    data.allStrapiArticle.edges.forEach(article => {
        const slug = article.node.slug;
        console.log("node.slug", article.node.slug);
            path: `/app/articles/${slug}`,
            component: require.resolve(`./src/templates/article.js`),
            context: { slug: slug },

Thank you!