If you’re developing a mobile app with Capacitor, React Native, Flutter, or any WebView-based framework, you’ve likely encountered the dreaded YouTube Error 150 or Error 153 when trying to embed YouTube videos. The player loads, but instead of your video, you see:
- “Video unavailable”
- “An error occurred. Please try again later. (Playback ID: …)”
- “This video is unavailable. Error code: 150/153”
This issue has affected thousands of developers across multiple platforms and frameworks. In this guide, we’ll explain exactly why this happens and provide a definitive solution using a CORS proxy.
Table of Contents
- Understanding YouTube Error 150/153
- Why This Affects Mobile Apps
- Affected Platforms and Frameworks
- The Root Cause: Missing Referer Headers
- The Solution: Using CORSPROXY
- Implementation Examples
- Troubleshooting
- Conclusion
Understanding YouTube Error 150/153
YouTube Error 150 and 153 are playback errors that occur when YouTube cannot verify the identity of the embedder. According to YouTube’s official documentation:
“YouTube must be able to verify the referrer URL of the embedded player to authorize playback.”
Error 150 typically means:
- The video has embedding restrictions
- The request origin cannot be verified
- The video owner has restricted playback on certain sites
Error 153 specifically indicates:
- Missing or invalid Referer header
- Incorrect Referrer-Policy preventing referer information from being sent
- The embedder identity is missing
This became significantly more prevalent after YouTube’s July 9, 2025 API update, which enforced stricter embedder identity verification.
Why This Affects Mobile Apps
Mobile apps using WebViews face a unique challenge: they don’t operate like traditional web browsers.
The WebView Problem
When you embed a YouTube video in a mobile app:
- Custom URL Schemes: Apps like Capacitor use custom protocols (
ionic://,capacitor://) instead ofhttp://orhttps:// - No Valid Referer: These custom schemes don’t send proper HTTP Referer headers
- YouTube Rejects the Request: Without a valid referer, YouTube blocks the video
iOS is Particularly Affected
Apple’s WKWebView (used by all iOS apps including Capacitor and React Native) has a known limitation documented in WebKit Bug 169846:
WKWebView often does not send the HTTP Referer header in cross-origin requests originating from dynamic content or injected iframes.
This means even if you set the correct Referrer-Policy, iOS may still strip the referer header when loading the YouTube iframe.
Android Differences
Android WebView handles referer headers differently and is generally more permissive. However, some Android devices and configurations still experience these issues, especially with newer YouTube API requirements.
Affected Platforms and Frameworks
This issue has been reported across virtually every WebView-based mobile development framework:
Capacitor / Ionic
The Capacitor issue #8205 documents this extensively. When Capacitor apps load content using the ionic:// protocol, no proper Referer header is sent.
React Native
React Native’s WebView component experiences the same issue. Multiple issues have been reported:
- Videos work in development but fail in production builds
- iOS experiences more failures than Android
- Error 150/153 appears on some devices but not others
Flutter
Flutter’s youtube_player_flutter and youtube_player_iframe packages have numerous open issues:
- Issue #1107: “Video unavailable. This video is unavailable”
- Issue #1125: “This video is unavailable Error code: 15”
- Issue #1112: “This video is unavailable Error code: 15”
Xamarin / .NET MAUI
The Xamarin.Forms issue #11504 documents how restricted YouTube videos show “Video is unavailable” when embedded in local HTML in WebView.
NW.js / Electron
Desktop apps using embedded browsers face the same issue. The NW.js issue #2856 has been open since 2014, showing this is a long-standing problem.
H5P / Moodle
Educational platforms using H5P for interactive content report the same issue when embedding YouTube videos in mobile apps: moodle-mod_hvp #602.
The Root Cause: Missing Referer Headers
Let’s understand the technical root cause:
How YouTube Verifies Embedders
When you embed a YouTube video, the request flow is:
Your App (ionic://localhost)
→ iframe src="youtube.com/embed/VIDEO_ID"
→ YouTube checks HTTP headers
→ Referer header is missing or invalid
→ Error 150/153
What YouTube Expects
YouTube requires one of the following:
- Valid HTTP/HTTPS Referer: A standard web URL that YouTube can verify
- Proper Referrer-Policy: The header
referrerpolicy="strict-origin-when-cross-origin"or similar
What Mobile Apps Send
Mobile apps often send:
- No Referer: Custom schemes like
ionic://are stripped by the browser - Invalid Referer:
file://orcapacitor://which YouTube doesn’t recognize - Blocked by Policy: Some security configurations block cross-origin referer headers
The Solution: Using CORSPROXY
The most reliable solution is to proxy the YouTube embed through a CORS proxy that provides proper HTTP headers. This bypasses the WebView’s limitations entirely.
How It Works
Instead of loading the YouTube embed directly:
<!-- This FAILS in mobile apps -->
<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ"></iframe>
You proxy it through corsproxy.io:
<!-- This WORKS in mobile apps -->
<iframe src="https://corsproxy.io/?url=https://www.youtube.com/embed/dQw4w9WgXcQ"></iframe>
Why This Works
- Your app requests the proxy (not YouTube directly)
- The proxy fetches YouTube from a server with proper HTTP headers
- YouTube sees a valid referer and allows playback
- The proxy returns the content to your app
Implementation Examples
Capacitor / Ionic (Angular)
// youtube-embed.component.ts
import { Component, Input } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
@Component({
selector: 'app-youtube-embed',
template: `
<iframe
[src]="safeUrl"
frameborder="0"
allowfullscreen
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture">
</iframe>
`
})
export class YoutubeEmbedComponent {
@Input() videoId: string = '';
get safeUrl(): SafeResourceUrl {
const youtubeUrl = `https://www.youtube.com/embed/${this.videoId}`;
const proxiedUrl = `https://corsproxy.io/?url=${encodeURIComponent(youtubeUrl)}`;
return this.sanitizer.bypassSecurityTrustResourceUrl(proxiedUrl);
}
constructor(private sanitizer: DomSanitizer) {}
}
React Native
// YouTubePlayer.tsx
import React from 'react';
import { WebView } from 'react-native-webview';
import { View, StyleSheet } from 'react-native';
interface YouTubePlayerProps {
videoId: string;
}
export const YouTubePlayer: React.FC<YouTubePlayerProps> = ({ videoId }) => {
const youtubeUrl = `https://www.youtube.com/embed/${videoId}`;
const proxiedUrl = `https://corsproxy.io/?url=${encodeURIComponent(youtubeUrl)}`;
return (
<View style={styles.container}>
<WebView
source={{ uri: proxiedUrl }}
allowsFullscreenVideo
javaScriptEnabled
domStorageEnabled
mediaPlaybackRequiresUserAction={false}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
height: 200,
width: '100%',
},
});
Flutter
// youtube_player_widget.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class YouTubePlayerWidget extends StatefulWidget {
final String videoId;
const YouTubePlayerWidget({Key? key, required this.videoId}) : super(key: key);
@override
State<YouTubePlayerWidget> createState() => _YouTubePlayerWidgetState();
}
class _YouTubePlayerWidgetState extends State<YouTubePlayerWidget> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
final youtubeUrl = 'https://www.youtube.com/embed/${widget.videoId}';
final proxiedUrl = 'https://corsproxy.io/?url=${Uri.encodeComponent(youtubeUrl)}';
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..loadRequest(Uri.parse(proxiedUrl));
}
@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 16 / 9,
child: WebViewWidget(controller: _controller),
);
}
}
Plain HTML / JavaScript
<!DOCTYPE html>
<html>
<head>
<title>YouTube Embed</title>
</head>
<body>
<div id="player"></div>
<script>
function embedYouTube(videoId, containerId) {
const container = document.getElementById(containerId);
const youtubeUrl = `https://www.youtube.com/embed/${videoId}`;
const proxiedUrl = `https://corsproxy.io/?url=${encodeURIComponent(youtubeUrl)}`;
const iframe = document.createElement('iframe');
iframe.src = proxiedUrl;
iframe.width = '100%';
iframe.height = '315';
iframe.frameBorder = '0';
iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
iframe.allowFullscreen = true;
container.appendChild(iframe);
}
// Usage
embedYouTube('dQw4w9WgXcQ', 'player');
</script>
</body>
</html>
Xamarin / .NET MAUI
// YouTubePlayerPage.xaml.cs
using Microsoft.Maui.Controls;
using System.Web;
public partial class YouTubePlayerPage : ContentPage
{
public YouTubePlayerPage(string videoId)
{
InitializeComponent();
var youtubeUrl = $"https://www.youtube.com/embed/{videoId}";
var proxiedUrl = $"https://corsproxy.io/?url={HttpUtility.UrlEncode(youtubeUrl)}";
var webView = new WebView
{
Source = new UrlWebViewSource { Url = proxiedUrl },
HeightRequest = 200
};
Content = webView;
}
}
Troubleshooting
Video Still Not Playing?
- Check the Video ID: Ensure you’re using the correct 11-character YouTube video ID
- Verify URL Encoding: Always use
encodeURIComponent()or equivalent - Test the Proxied URL: Open the proxied URL in a browser to verify it works
- Check Network Connectivity: Ensure your app can reach corsproxy.io
iOS-Specific Issues
If videos still fail on iOS:
// Ensure proper WebView configuration for iOS
const webViewConfig = {
allowsInlineMediaPlayback: true,
mediaPlaybackRequiresUserAction: false,
allowsFullscreenVideo: true,
};
Android-Specific Issues
For Android, ensure hardware acceleration is enabled:
<!-- AndroidManifest.xml -->
<application android:hardwareAccelerated="true">
Age-Restricted or Region-Locked Videos
Some videos have additional restrictions that cannot be bypassed:
- Age-restricted content may require authentication
- Region-locked videos are blocked regardless of proxy
Why CORSPROXY.io is the Best Solution
Compared to Other Workarounds
| Approach | Works on iOS | Works on Android | Easy to Implement | No Server Required |
|---|---|---|---|---|
| CORSPROXY | Yes | Yes | Yes | Yes |
| Custom Backend Proxy | Yes | Yes | No | No |
| Referrer-Policy Header | No | Sometimes | Yes | Yes |
| Base URL Workaround | No | Sometimes | Yes | Yes |
| Native YouTube SDK | Sometimes | Yes | No | Yes |
Benefits of Using CORSPROXY
- No server infrastructure required - Works immediately
- Cross-platform compatible - Same solution for iOS, Android, and desktop
- Simple implementation - Just change the URL
- Reliable - Handles all the header complexities for you
- Fast - Global CDN ensures low latency
Conclusion
YouTube Error 150/153 in mobile apps is caused by WebView limitations that prevent proper HTTP Referer headers from being sent. This is a fundamental limitation of how mobile apps handle embedded web content.
The definitive solution is to proxy YouTube embeds through CORSPROXY:
<iframe src="https://corsproxy.io/?url=https://www.youtube.com/embed/YOUR_VIDEO_ID"></iframe>
This simple change resolves the issue across all platforms:
- Capacitor / Ionic
- React Native
- Flutter
- Xamarin / .NET MAUI
- NW.js / Electron
- Any WebView-based application
Stop struggling with workarounds that don’t work. Use corsproxy.io and get your YouTube videos playing reliably in your mobile apps.
Have you encountered other YouTube embedding issues in your mobile app? Share your experience in the comments or reach out for help with specific implementations.