Issue with redirects not consistently working on Netlify deployment.

21 replies
Last updated: Mar 10, 2021
Hey there,
I’m wondering if anyone has any advice on redirects. I followed the advice in the
help channel, and it was really good information. I can’t find the thread now, though.
Anyway, I leaned very heavily on
user L
’s gist , and my code looks like this:

redirect.js

export default {
    title: "Redirect",
    name: "redirect",
    // __experimental_actions: ['update', 'create', 'delete', 'publish'],
    type: "document",
    fields: [
        {
            title: "Internal Name",
            name: "internalName",
            type: "string",
            validation: Rule => Rule.required(),
        },
        {
            title: "Match Rule",
            description:
                "The URL path to match. This is the "from" portion of the redirect. A "splat" (*) can be used to match any additional characters at the end of the line (e.g. `/blog/*`).",
            name: "rule",
            type: "string",
            validation: Rule =>
                Rule.required().regex(/^\//, { name: "Must start with a `/`" }),
        },
        {
            title: "Redirect Target",
            description:
                "The destination for the redirect. If a "splat" (*) was used in the match rule, it can be expanded in the target using `:splat` (e.g. `/blog/:splat`).",
            name: "target",
            type: "string",
            validation: Rule => Rule.required().regex(/^(\/|https?:\/\/)/),
        },
        {
            title: "Redirect Type",
            description:
                "Both temporary and permanent redirects can be internal or external links. Rewrites can only be internal (i.e. to another page that is built in Sanity).",
            name: "statusCode",
            type: "string",
            options: {
                list: [
                    { title: "Temporary (302)", value: "302" },
                    { title: "Permanent (301)", value: "301" },
                    { title: "Rewrite (200)", value: "200" },
                ],
            },
            validation: Rule => Rule.required(),
        },
    ],
    preview: {
        select: {
            title: "internalName",
            rule: "rule",
            target: "target",
            statusCode: "statusCode",
        },
        prepare: ({ title, rule, target, statusCode }) => ({
            title,
            subtitle: `${rule} ${target} ${statusCode}`,
        }),
    },
}

gatsby-node.js

const fs = require('fs')
const path = require('path')

exports.onPostBuild = ({ graphql }) => {
	graphql(`
		{
			redirects: allSanityRedirect {
				nodes {
					target
					rule
					statusCode
				}
			}
		}
	`).then(({ data }) => {
		const redirectData = data.redirects.nodes
			.map(({ rule, target, statusCode }) => `${rule} ${target} ${statusCode}`)
			.join('\n')

		console.log('*********************************************')
		console.log('redirectData: ', redirectData)
		console.log('*********************************************')
		return fs.appendFile(path.resolve(__dirname, 'public/_redirects'), redirectData, (err) => {
			if (err) throw err
			console.log('redirect error:', err)
		})
	})
}
When I deploy this to Netlify,
sometimes the redirect rules are processed, and other times it’s completely ignored. I can’t figure out what changes from one deploy to the next. There is a console.log that prints out the redirects, and they also show in the console regardless of whether or not the redirects are actually created. Can anyone see anything wrong with the code, or help me to see what I’m missing?
Mar 9, 2021, 7:38 PM
so while this won’t solve your netlify, you can use createRedirect in gatsby instead of writing to the _redirects file
Mar 9, 2021, 7:40 PM
redirects.forEach(redirect => {
      actions.createRedirect({
        fromPath: redirect.fromPath,
        toPath: redirect.toPath,
        isPermanent: redirect.statusCode === 301, // use as fallback. this is part of Gatsby's API
        statusCode: redirect.statusCode || 302 // Netlify specific. Will override `isPermanent`
      });
    });
Mar 9, 2021, 7:41 PM
similar to how you would do createPage
Mar 9, 2021, 7:41 PM
(not sure if this is changing in v3 gatsby)
Mar 9, 2021, 7:41 PM
if you are using any cache plugins gatsby may be skipping the _redirect? i’ve had some issues when I write files like you are
Mar 9, 2021, 7:42 PM
and it seems relatively hard to debug or resolve
Mar 9, 2021, 7:42 PM
Not using any cache plugins, and I was clearing my cache with the build command, but I removed that to see if that’s what was causing the issue.
Mar 9, 2021, 7:43 PM
It didn’t help, though.
Mar 9, 2021, 7:43 PM
createRedirect is client side only. If you’re able to do server side redirects you’ll get much better behavior and performance. I’ll take a look at the above when I’m at my computer shortly.
Mar 9, 2021, 7:45 PM
Awesome! Thank you both!
Mar 9, 2021, 7:46 PM
Oh, it’s a classic race condition. You’re wrapping the
onPostBuild
function in curly brackets, which has the effect of not returning the promise created by
graphql()
which means Gatsby might finish before it completes. That’s why it’s intermittent. Either remove the curly brackets or add an explicit
return
statement.
Mar 9, 2021, 8:06 PM
Okay, that makes sense, and sounds easy enough. I pulled out the curlies, and it fails citing that graphql is not a function. Where should I add the return?
Mar 9, 2021, 8:59 PM
We were just implementing something similar the other day. If you create the redirects as
user G
suggested and also use gatsby-plugin-netlify, they will be appended to the _redirects file automatically: https://www.gatsbyjs.com/plugins/gatsby-plugin-netlify/#redirects
Mar 9, 2021, 9:17 PM
Just to make sure what I’m saying is clear:
const fs = require("fs");
const path = require("path");

exports.onPostBuild = ({ graphql }) =>
  graphql(`
    {
      redirects: allSanityRedirect {
        nodes {
          target
          rule
          statusCode
        }
      }
    }
  `).then(({ data }) => {
    const redirectData = data.redirects.nodes
      .map(({ rule, target, statusCode }) => `${rule} ${target} ${statusCode}`)
      .join("\n");
    console.log("*********************************************");
    console.log("redirectData: ", redirectData);
    console.log("*********************************************");
    return fs.appendFile(
      path.resolve(__dirname, "public/_redirects"),
      redirectData,
      (err) => {
        if (err) throw err;
        console.log("redirect error:", err);
      }
    );
  });
Mar 9, 2021, 10:24 PM
user Q
Oh cool, good to know! Any idea if it works with external URLs?
Mar 9, 2021, 10:27 PM
Oh, man... not having any luck with this at all.
I applied
user L
’s code update with the curly brackets removed, and it’s still losing the Gatsby race more often than not, unfortunately.
The
user G
/
user Q
solution shows that the redirects have been processed, but they’re still not actually working.
The code looks like this:


async function generateRedirects(graphql, actions, reporter) {
	const result = await graphql(`
		{
			redirects: allSanityRedirect {
				nodes {
					target
					rule
					statusCode
				}
			}
		}
	`)

	if (result.errors) throw result.errors

	const redirects = (result.data.redirects || {}).nodes || []

	redirects.forEach((redirect) => {
		<http://reporter.info|reporter.info>(`Creating Redirect: ${redirect.rule} to ${redirect.target}`)
		actions.createRedirect({
			fromPath: redirect.rule,
			toPath: redirect.target,
			isPermanent: redirect.statusCode === 301, // use as fallback. this is part of Gatsby's API
			statusCode: redirect.statusCode || 302, // Netlify specific. Will override `isPermanent`
			force: true,
		})
	})
}

exports.createPages = async ({ graphql, actions, reporter }) => {
	await generateRedirects(graphql, actions, reporter)
	await createLandingPages('/', graphql, actions, reporter)
}
Once the deploy is completed, I am able to download it, and view the redirects file, but if I actually test it, there’s no redirect.

This seems like more of an issue with Netlify, and I probably have misread the docs, or something like that. I haven’t had much luck with the Netlify forums. Can anyone see any obvious reason this might not work?
Mar 10, 2021, 1:49 AM
Oh geez, I’m sorry… you need to use the Promise versions of
fs
. I.e. change your require line to
const fs = require("fs").promises
. Otherwise
fs.writeFile
expects a callback function.
Mar 10, 2021, 3:01 AM
Or you can do
fs.promises.writeFile(…)
Mar 10, 2021, 3:03 AM
You, Sir, are a brilliant genius! 💡
Thank you all for your help with this!
Mar 10, 2021, 3:30 AM
You bet! Glad you finally got it working. 🙂
Mar 10, 2021, 3:27 PM
user B
you probably ran into this bug with the other solution: https://github.com/gatsbyjs/gatsby/pull/30155 – but seems like you found a solution already 👌
user L
yes, also works with external urls
Mar 10, 2021, 8:22 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?