Fix: useFetch URL Must Not Start With // in Nuxt

Error message:
[nuxt] [useFetch] the request URL must not start with \"//\".
data fetching 2025-01-25

What Causes This Error?

This error occurs when you use a protocol-relative URL (starting with //) in useFetch or $fetch. Nuxt blocks these URLs because they can cause security and SSR issues.

The Problem

// ❌ Wrong - protocol-relative URL
const { data } = await useFetch('//api.example.com/data')

// ❌ Also wrong
const { data } = await useFetch('//example.com/api/users')

Why Protocol-Relative URLs Are Blocked

  1. SSR Issues - On the server, there’s no “current protocol” to inherit
  2. Security - Could accidentally switch between HTTP/HTTPS
  3. Ambiguity - Behavior differs between client and server

The Fix

Use explicit protocols:

// ✅ Correct - explicit HTTPS
const { data } = await useFetch('https://api.example.com/data')

// ✅ Correct - explicit HTTP (if needed)
const { data } = await useFetch('http://api.example.com/data')

Common Scenarios

API URL from Config

// ❌ Wrong - might have //
const apiUrl = config.apiUrl // Could be '//api.example.com'
const { data } = await useFetch(`${apiUrl}/users`)

// ✅ Correct - ensure protocol
const apiUrl = config.apiUrl.startsWith('//')
  ? `https:${config.apiUrl}`
  : config.apiUrl
const { data } = await useFetch(`${apiUrl}/users`)

Dynamic URLs

// ✅ Helper function to ensure protocol
function ensureProtocol(url: string): string {
  if (url.startsWith('//')) {
    return `https:${url}`
  }
  return url
}

const { data } = await useFetch(ensureProtocol(dynamicUrl))

Environment Variables

# .env
# ❌ Wrong
API_URL=//api.example.com

# ✅ Correct
API_URL=https://api.example.com
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      apiUrl: process.env.API_URL || 'https://api.example.com'
    }
  }
})

Using Base URL

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      apiBase: 'https://api.example.com'
    }
  }
})

// In component
const config = useRuntimeConfig()
const { data } = await useFetch('/users', {
  baseURL: config.public.apiBase
})

For Internal APIs

For Nuxt server routes, use relative paths:

// ✅ Relative path for internal API
const { data } = await useFetch('/api/users')

// This automatically resolves to the correct URL

Migration from Protocol-Relative URLs

If you’re migrating code that used // URLs:

// Old code
const url = '//api.example.com/data'

// New code - add protocol
const url = 'https://api.example.com/data'

// Or dynamically
const url = `${window.location.protocol}//api.example.com/data`
// Note: This only works on client-side

SSR-Safe Protocol Detection

// composables/useApiUrl.ts
export function useApiUrl(path: string) {
  const config = useRuntimeConfig()

  // Always use explicit protocol from config
  return `${config.public.apiBase}${path}`
}

// Usage
const { data } = await useFetch(useApiUrl('/users'))

Quick Reference

URL FormatAllowed?Fix
//example.com/api❌ NoAdd https: prefix
https://example.com/api✅ Yes-
http://example.com/api✅ Yes-
/api/users✅ Yes-
./api/users✅ Yes-

Quick Checklist

  • All external URLs start with https:// or http://
  • Environment variables use explicit protocols
  • No // at start of any URL strings
  • Config values validated for proper protocol
  • Dynamic URLs are sanitized before use