Fix: Cannot Navigate to URL With Protocol in Nuxt

Error message:
Cannot navigate to a URL with '
navigation 2025-01-25

What Causes This Error?

This security error occurs when you try to navigate to a URL with a dangerous protocol like javascript:, data:, or vbscript:. Nuxt blocks these to prevent XSS (Cross-Site Scripting) attacks.

Blocked Protocols

ProtocolReason Blocked
javascript:Can execute arbitrary code
data:Can contain malicious payloads
vbscript:Legacy script execution

Common Scenarios and Fixes

Scenario 1: Accidentally Using javascript: URL

// ❌ Wrong - javascript: protocol
navigateTo('javascript:void(0)')
navigateTo('javascript:alert("hi")')

// ✅ Correct - use function instead
const doSomething = () => {
  // Your logic here
}

Scenario 2: Dynamic URL from User Input

// ❌ Dangerous - user could inject javascript:
const userUrl = getUserInput() // Could be 'javascript:alert(1)'
navigateTo(userUrl)

// ✅ Safe - validate URL first
const userUrl = getUserInput()
if (isValidUrl(userUrl)) {
  navigateTo(userUrl, { external: true })
}

function isValidUrl(url: string): boolean {
  try {
    const parsed = new URL(url)
    return ['http:', 'https:'].includes(parsed.protocol)
  } catch {
    return false
  }
}
// ❌ Dangerous - CMS content could be compromised
const link = cmsContent.link
navigateTo(link)

// ✅ Safe - sanitize and validate
function sanitizeUrl(url: string): string | null {
  try {
    const parsed = new URL(url)
    if (['http:', 'https:', 'mailto:', 'tel:'].includes(parsed.protocol)) {
      return url
    }
    return null
  } catch {
    // Relative URLs are safe
    if (url.startsWith('/') && !url.startsWith('//')) {
      return url
    }
    return null
  }
}

const safeUrl = sanitizeUrl(cmsContent.link)
if (safeUrl) {
  navigateTo(safeUrl, { external: safeUrl.startsWith('http') })
}

Scenario 4: Data URLs for Downloads

// ❌ Won't work with navigateTo
const dataUrl = 'data:text/plain;base64,SGVsbG8='
navigateTo(dataUrl)

// ✅ Use download approach instead
function downloadFile(content: string, filename: string) {
  const blob = new Blob([content], { type: 'text/plain' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = filename
  a.click()
  URL.revokeObjectURL(url)
}

Allowed Protocols

These protocols work with navigateTo:

// ✅ HTTP/HTTPS (with external: true)
navigateTo('https://example.com', { external: true })

// ✅ Relative paths
navigateTo('/about')
navigateTo('/products/123')

// ✅ Internal routes
navigateTo({ name: 'products-id', params: { id: 123 } })

Use regular anchor tags instead of navigateTo:

<template>
  <!-- ✅ mailto: links -->
  <a href="mailto:hello@example.com">Email Us</a>

  <!-- ✅ tel: links -->
  <a href="tel:+1234567890">Call Us</a>
</template>

Or with programmatic approach:

// For mailto/tel, use window.location directly
if (process.client) {
  window.location.href = 'mailto:hello@example.com'
}

URL Validation Utility

Create a reusable utility for safe navigation:

// utils/navigation.ts
const SAFE_PROTOCOLS = ['http:', 'https:', 'mailto:', 'tel:']

export function safeNavigate(url: string) {
  // Handle relative URLs
  if (url.startsWith('/') && !url.startsWith('//')) {
    return navigateTo(url)
  }

  // Validate absolute URLs
  try {
    const parsed = new URL(url)

    if (SAFE_PROTOCOLS.includes(parsed.protocol)) {
      if (['http:', 'https:'].includes(parsed.protocol)) {
        return navigateTo(url, { external: true })
      }
      // mailto: and tel: need direct approach
      if (process.client) {
        window.location.href = url
      }
    } else {
      console.warn('Blocked navigation to unsafe URL:', url)
    }
  } catch (e) {
    console.error('Invalid URL:', url)
  }
}

Security Best Practices

  1. Never trust user input - Always validate URLs before navigation
  2. Whitelist protocols - Only allow known-safe protocols
  3. Sanitize CMS content - External content could be compromised
  4. Use CSP headers - Content Security Policy adds extra protection
// nuxt.config.ts - Add CSP headers
export default defineNuxtConfig({
  routeRules: {
    '/**': {
      headers: {
        'Content-Security-Policy': "default-src 'self'; script-src 'self'"
      }
    }
  }
})

Quick Checklist

  • No javascript: URLs in navigation calls
  • User-provided URLs are validated
  • CMS/database URLs are sanitized
  • Use anchor tags for mailto: and tel:
  • External URLs use { external: true }
  • URL validation function in place