Back to Blog

Fix YouTube Error 150/153 in Capacitor, React Native, Flutter & WebViews

Getting YouTube Error 150 or 153 "Video unavailable" in your mobile app? Learn why YouTube embeds fail in iOS/Android WebViews and how to fix it with a simple CORS proxy solution.

Fix YouTube Error 150/153 in Capacitor, React Native, Flutter & WebViews

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

  1. Understanding YouTube Error 150/153
  2. Why This Affects Mobile Apps
  3. Affected Platforms and Frameworks
  4. The Root Cause: Missing Referer Headers
  5. The Solution: Using CORSPROXY
  6. Implementation Examples
  7. Troubleshooting
  8. 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:

  1. Custom URL Schemes: Apps like Capacitor use custom protocols (ionic://, capacitor://) instead of http:// or https://
  2. No Valid Referer: These custom schemes don’t send proper HTTP Referer headers
  3. 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:

  1. Valid HTTP/HTTPS Referer: A standard web URL that YouTube can verify
  2. Proper Referrer-Policy: The header referrerpolicy="strict-origin-when-cross-origin" or similar

What Mobile Apps Send

Mobile apps often send:

  1. No Referer: Custom schemes like ionic:// are stripped by the browser
  2. Invalid Referer: file:// or capacitor:// which YouTube doesn’t recognize
  3. 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

  1. Your app requests the proxy (not YouTube directly)
  2. The proxy fetches YouTube from a server with proper HTTP headers
  3. YouTube sees a valid referer and allows playback
  4. 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?

  1. Check the Video ID: Ensure you’re using the correct 11-character YouTube video ID
  2. Verify URL Encoding: Always use encodeURIComponent() or equivalent
  3. Test the Proxied URL: Open the proxied URL in a browser to verify it works
  4. 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

ApproachWorks on iOSWorks on AndroidEasy to ImplementNo Server Required
CORSPROXYYesYesYesYes
Custom Backend ProxyYesYesNoNo
Referrer-Policy HeaderNoSometimesYesYes
Base URL WorkaroundNoSometimesYesYes
Native YouTube SDKSometimesYesNoYes

Benefits of Using CORSPROXY

  1. No server infrastructure required - Works immediately
  2. Cross-platform compatible - Same solution for iOS, Android, and desktop
  3. Simple implementation - Just change the URL
  4. Reliable - Handles all the header complexities for you
  5. 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.

Related blog posts

Create a free Account to fix CORS Errors in Production

Say goodbye to CORS errors and get back to building great web applications. It's free!

CORSPROXY Dashboard