Fix: Navigating to External URL Not Allowed in Nuxt

Error message:
Navigating to an external URL is not allowed by default. Use `navigateTo(url, { external: true })`.
navigation 2025-01-25

What Causes This Error?

This error occurs when you try to navigate to an external URL (a URL on a different domain) using Nuxt’s navigateTo() function without explicitly allowing external navigation.

Nuxt blocks external navigation by default as a security measure to prevent unintended redirects to malicious sites.

The Fix

Add { external: true } to your navigateTo() call:

// ❌ Wrong - will throw an error
navigateTo('https://google.com')

// ✅ Correct - explicitly allow external navigation
navigateTo('https://google.com', { external: true })

Common Scenarios

Redirecting After Authentication

// In your login handler or middleware
const handleLogin = async () => {
  await login()

  // Redirect to external dashboard
  navigateTo('https://dashboard.example.com', { external: true })
}

OAuth Redirects

// Redirecting to OAuth provider
const loginWithGoogle = () => {
  navigateTo('https://accounts.google.com/oauth/authorize?...', {
    external: true
  })
}

Payment Gateway Redirects

// Redirecting to payment processor
const redirectToPayment = (paymentUrl: string) => {
  navigateTo(paymentUrl, { external: true })
}

In Middleware

// middleware/redirect.ts
export default defineNuxtRouteMiddleware((to) => {
  if (to.path === '/old-external') {
    return navigateTo('https://new-site.com', { external: true })
  }
})

Alternative: Use Window Location

For simple external redirects, you can also use the native browser API:

// Client-side only
if (process.client) {
  window.location.href = 'https://external-site.com'
}

Or with a link:

<template>
  <!-- For user-initiated navigation, just use a regular link -->
  <a href="https://external-site.com" target="_blank" rel="noopener">
    Visit External Site
  </a>
</template>

Additional Options

navigateTo() accepts several options for external navigation:

navigateTo('https://example.com', {
  external: true,
  replace: true,        // Replace current history entry
  redirectCode: 301,    // HTTP redirect code (server-side)
  open: {               // Open in new window/tab
    target: '_blank',
    windowFeatures: {
      width: 500,
      height: 500
    }
  }
})

Open in New Tab

// Open external URL in new tab
navigateTo('https://example.com', {
  external: true,
  open: {
    target: '_blank'
  }
})

Server-Side Redirects

For server-side redirects (in API routes or server middleware):

// server/api/redirect.ts
export default defineEventHandler((event) => {
  return sendRedirect(event, 'https://external-site.com', 302)
})

Security Considerations

Nuxt blocks external navigation by default because:

  1. Prevents open redirects - Attackers can’t trick users into visiting malicious sites
  2. Explicit intent - Forces developers to consciously allow external redirects
  3. Audit trail - Easy to search for external: true in codebase

Best Practices

// ✅ Validate external URLs before redirecting
const safeExternalRedirect = (url: string) => {
  const allowedDomains = ['trusted-site.com', 'partner-site.com']
  const urlObj = new URL(url)

  if (allowedDomains.includes(urlObj.hostname)) {
    navigateTo(url, { external: true })
  } else {
    console.error('Redirect to untrusted domain blocked:', url)
  }
}

Quick Reference

Navigation TypeMethod
Internal routenavigateTo('/about')
External URLnavigateTo('https://...', { external: true })
New tabnavigateTo('https://...', { external: true, open: { target: '_blank' } })
Native redirectwindow.location.href = '...'