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
- SSR Issues - On the server, there’s no “current protocol” to inherit
- Security - Could accidentally switch between HTTP/HTTPS
- 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 Format | Allowed? | Fix |
|---|---|---|
//example.com/api | ❌ No | Add 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://orhttp:// - Environment variables use explicit protocols
- No
//at start of any URL strings - Config values validated for proper protocol
- Dynamic URLs are sanitized before use