redirect-module icon indicating copy to clipboard operation
redirect-module copied to clipboard

redirect loop

Open toddpadwick opened this issue 2 years ago • 6 comments

I have the following redirect which seem to cause an infinite redirect loop. I can't quite figure out why. Do you know why this might be happening? If not, is there a way to set up some kind of log or debugger to track what the redirect module is actually doing behind the scenes for each redirect?

This is the rule: /get-the-positive-edge => /forms/get-the-positive-edge

And this is what happens when I enter /get-the-positive-edge into the browser: /forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/forms/get-the-positive-edge

toddpadwick avatar Jan 31 '22 16:01 toddpadwick

Experiencing the same issue, but only on one redirect in thousands.

Webbist-dev avatar Feb 17 '22 20:02 Webbist-dev

So we ditched this module and wrote our own.

In server middleware we have


module.exports = function (req, res, next) {
  const host = req.headers.host
  const env = process.env.NODE_ENV
  const notWww = host.slice(0, 4) !== 'www.'
  const isDomain = host.includes('domain.com')
  const redirect = redirects.find(r => r.from === req.url)

  if (env === 'production' && isDomain && notWww) {
    const newHost = 'www.' + host
    if (redirect) {
      const newLocation = redirect.to

      res.writeHead(parseInt(redirect.statusCode), {
        Location: 'https://' + newHost + newLocation
      })
      res.end()
    } else {
      res.writeHead(301, {
        Location: 'https://' + newHost + req.url
      })
      res.end()
    }
  } else if (redirect) {
    const newLocation = redirect.to

    res.writeHead(parseInt(redirect.statusCode), {
      Location: newLocation
    })
    res.end()
  } else {
    next()
  }
}

This works for all redirect objects passed to it, extensively tested on over 4k varying redirects. This also handles redirects for non-www to www and HTTP to HTTPS.

Webbist-dev avatar Feb 18 '22 17:02 Webbist-dev

Fantastic. thank you @Webbist-dev . So do you query and store your redirects on build in the same way as the redirect-module? We're currently doing a GQL query to get all our redirects in nuxt.config

toddpadwick avatar Feb 21 '22 09:02 toddpadwick

@Webbist-dev could you explain what the methods inside if (env === 'production' && isDomain && notWww) are for? Trying to work it out so I can adapt for our own needs. I am wondering if we can ditch that whole conditional considering we have some redirects that go to other sites and occasional some that do not have www.

Also presumably this does not accept regex redirects?

toddpadwick avatar Mar 03 '22 14:03 toddpadwick

Inspired by the code supplied by @Webbist-dev , I've found an adjusted working solution for our setup to meet the following requirements:

  1. query redirects from an endpoint on our CMS
  2. Support RegEx when matching redirects
  3. Allow redirects to include URLs that are not on the current domain – eg /example => https://someotherdomain.com/example

My solution to replace the requirement for the Nuxt Redirect-Module:

OPTIONAL – for those who need to query an endpoint for redirects array, In /store/index.js I am fetching all redirects from retour plugin in Craft CMS, in nuxtServerInit – this only occurs once during the initial build. I am then formatting them using map to prepare them for use:

import Vuex from 'vuex';
import fetch from 'node-fetch';
import fs from 'fs';



const createStore = () => {
  return new Vuex.Store({
    actions: {
        async nuxtServerInit({commit}, {app, $config}) {
            
            if (process.env.NODE_ENV == 'dev') {
                
                // GET AND STORE REDIRECTS
                const baseUrl = process.env.API_BASE_URL; // get API url
                const endpoint = '/actions/retour/api/get-redirects'; // get endpoint path
                const api = baseUrl+endpoint;
                fetch(api, {
                    method: 'GET',
                    headers: { 'Content-Type': 'application/json' }
                })
                .then(result => result.json())
                .then(result => {
                    console.info('Redirects generated');
                    if (result) {
                        const formattedRedirects = result.map(redirect => { // map redirect array and reformat it for redirect module requirements
                            let formattedRedirect = {};
                            formattedRedirect.from = redirect.redirectSrcUrl
                            formattedRedirect.to = redirect.redirectDestUrl
                            formattedRedirect.statusCode = redirect.redirectHttpCode
                            return formattedRedirect
                        })
                        fs.writeFile('./server-middleware/assets/redirects.json', JSON.stringify(formattedRedirects), err => {
                            if (err) {
                                console.error('Error writing redirect file', err);
                            }
                        });
                    } else {
                        console.log('No redirects in result');
                    }
                });

            }
            
        },
    }
  });
};

export default createStore

REQUIRED – in /server-middleware/redirects.js I am importing the file I created in nuxtServerInit (but this can just be an array of redirects you generate however you like) and then querying it for redirects:

import redirects from './assets/redirects.json'; // import redirects file generated in build via /store/index.js

module.exports = function (req, res, next) {
  const url = req.url;
  const redirect = redirects.find(obj => new RegExp(`^${obj.from}`).test(url));  // RegEx check to find url in redirect array
  if (redirect) { // if redirect exists, redirect it
    const newLocation = redirect.to 
    res.writeHead(parseInt(redirect.statusCode), { // apply status code eg 301
      Location: newLocation
    })
    res.end()
  } else { 
    next()
  }
}

And lastly, in nuxt.config.js, include the server middleware file

  serverMiddleware:['~/server-middleware/redirects.js'],

toddpadwick avatar Mar 08 '22 14:03 toddpadwick

Hi,

I've been struggling with this issue too since days. I figured out with a solution that works for me

Before : { from: "/get-the-positive-edge", to: "/forms/get-the-positive-edge", statusCode: 301 } After : { from: '^/get-the-positive-edge$', to: '/forms/get-the-positive-edge', statusCode: 301 }

I don't know if there is some issue with the spelling between "simple quote / double quote", regex mode in the from attribute but it works like this, no need for a middleware.

Regards,

sylvan0s avatar Mar 23 '22 11:03 sylvan0s