Nuxt.js: Allow redirect to absolute URL

Created on 23 May 2017  路  9Comments  路  Source: nuxt/nuxt.js

Feature request: Allow redirects to absolute URLs, SSR and client-side.

We have an app (https://domainr.com) with several legacy URLs, some of which redirect to other websites. We鈥檙e exploring using Nuxt/Vue for our new website and would like to preserve this behavior.

Snippet to reproduce

~/pages/_domain/iana.vue

<script>
export default {
  fetch ({ params, redirect }) {
    redirect(301, `https://www.iana.org/domains/root/db/${params.domain}.html`)
  }
}
</script>

Steps to reproduce

Navigate to /com/iana

Expected

Redirect to https://www.iana.org/domains/root/db/com.html

(Click here to see expected behavior: https://domainr.com/com/iana)

What actually happens

Redirect to /https:/www.iana.org/domains/root/db/com.html (local URL)

Background

Nuxt only allows local redirects to a path, which is prepended with router.base here: https://github.com/nuxt/nuxt.js/blob/dev/lib/app/server.js#L39

This feature request is available on Nuxt.js community (#c661)
enhancement

Most helpful comment

Refined the hack (workaround?) a bit. It now works on server and client side, redirecting to an absolute URL. It overrides the Location header and sets the HTTP response code by [ab]using the error function: error({ statusCode: 301, ... }).

The only remaining issue is the extra history item if the redirect occurs client-side. I wasn鈥檛 able to find a way to hook into vue-router鈥檚 beforeEntry function.

function fixRedirect (ctx) {
  const _redirect = ctx.redirect
  ctx.redirect = function (status, path, query) {
    // If only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
    if (typeof status === 'string' && (typeof path === 'undefined' || typeof path === 'object')) {
      query = path || {}
      path = status
      status = 302
    }
    path = addQuery(path, query)
    if (!isAbsolute(path)) {
      _redirect(status, path)
    } else if (ctx.isServer && ctx.res) {
      ctx.res.setHeader('Location', path)
      ctx.error({ statusCode: status, message: `Redirecting to ${path}` })
    } else if (ctx.isClient && window && window.location) {
      window.location.replace(path)
      // This doesn鈥檛 seem to be necessary in more recent builds of Nuxt
      // ctx.error({ statusCode: status, message: `Redirecting to ${path}` })
    }
  }
}

As a bonus, it allows redirect-only pages like this:

pages/_domain/iana.vue

<script>
export default {
  fetch ({ params, redirect }) {
    redirect(301, `https://www.iana.org/domains/root/db/${params.domain}.html`)
  }
}
</script>

All 9 comments

I was able to hack a workaround using middleware, but it throws errors on the client and server side. It throws errors and adds an HTML5 history entry for the redirect. Is there a way to stop processing the request process in middleware, hook into vue-router at a lower-level, or use normal vue-router redirects?

https://gist.github.com/ydnar/afd2fdfa90f9579a8188f0bfe418493d

The hack replaces context.redirect:

function fixRedirect (ctx) {
  const _redirect = ctx.redirect
  ctx.redirect = function (status, path, query) {
    ctx._redirected = true // Nuxt-specific, unnecessary?
    // If only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
    if (typeof status === 'string' && (typeof path === 'undefined' || typeof path === 'object')) {
      query = path || {}
      path = status
      status = 302
    }
    path = addQuery(path, query)
    if (!isAbsolute(path)) {
      _redirect(status, path)
    } else if (ctx.isServer && ctx.res) {
      ctx.res.writeHead(status, { 'Location': path })
      ctx.res.writeHead = function () {} // FIXME: this is evil
      ctx.res.end()
      ctx.res.end = function () {} // Also evil
      _redirect(status, path)
    } else if (ctx.isClient && window && window.location) {
      window.location = path
      ctx.error()
    }
  }
}

Refined the hack (workaround?) a bit. It now works on server and client side, redirecting to an absolute URL. It overrides the Location header and sets the HTTP response code by [ab]using the error function: error({ statusCode: 301, ... }).

The only remaining issue is the extra history item if the redirect occurs client-side. I wasn鈥檛 able to find a way to hook into vue-router鈥檚 beforeEntry function.

function fixRedirect (ctx) {
  const _redirect = ctx.redirect
  ctx.redirect = function (status, path, query) {
    // If only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
    if (typeof status === 'string' && (typeof path === 'undefined' || typeof path === 'object')) {
      query = path || {}
      path = status
      status = 302
    }
    path = addQuery(path, query)
    if (!isAbsolute(path)) {
      _redirect(status, path)
    } else if (ctx.isServer && ctx.res) {
      ctx.res.setHeader('Location', path)
      ctx.error({ statusCode: status, message: `Redirecting to ${path}` })
    } else if (ctx.isClient && window && window.location) {
      window.location.replace(path)
      // This doesn鈥檛 seem to be necessary in more recent builds of Nuxt
      // ctx.error({ statusCode: status, message: `Redirecting to ${path}` })
    }
  }
}

As a bonus, it allows redirect-only pages like this:

pages/_domain/iana.vue

<script>
export default {
  fetch ({ params, redirect }) {
    redirect(301, `https://www.iana.org/domains/root/db/${params.domain}.html`)
  }
}
</script>

I'm adding it for Nuxt 1.0 @ydnar :)

Estimation when will it be released?

Thanks for fixing!

I found a bug in the 1.x.x implementation: the client-side redirect adds an additional history item. Our internal workaround uses window.location.replace(path) instead of window.location = path.

Should I file a new bug?

I can't redirect inside a middleware, it returns an ERR_REDIRECT but after that it redirect to desired page.
Anything I can do to don't send to this error page?

seems to happen to me too.

Any ETA on this bug fix?

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vadimsg picture vadimsg  路  3Comments

maicong picture maicong  路  3Comments

VincentLoy picture VincentLoy  路  3Comments

vadimsg picture vadimsg  路  3Comments

gary149 picture gary149  路  3Comments