Back to Blog

Unity WebGL CORS Error: Complete Fix Guide for Game Developers

Learn how to fix "Access to XMLHttpRequest has been blocked by CORS policy" errors in Unity WebGL builds. Covers server configuration, UnityWebRequest, and proxy solutions.

Unity WebGL CORS Error: Complete Fix Guide for Game Developers

Unity WebGL games run directly in the browser, which means they’re subject to the same security restrictions as any other web application. One of the most common issues developers face is the dreaded CORS error when their WebGL build tries to communicate with external APIs or load resources from different domains. This guide covers everything you need to know to diagnose and fix Unity WebGL CORS errors.

Table of Contents

  1. Understanding CORS in Unity WebGL
  2. Why Unity WebGL Gets CORS Errors
  3. Common CORS Error Messages
  4. Server-Side Solutions
  5. Using a CORS Proxy
  6. Unity-Specific Configuration
  7. Debugging CORS Issues
  8. Best Practices

Understanding CORS in Unity WebGL

When you build a Unity project for WebGL, Unity compiles your C# code to WebAssembly and JavaScript. The UnityWebRequest and legacy WWW classes are implemented using the browser’s XMLHttpRequest API under the hood. This means your Unity game is bound by the browser’s Same-Origin Policy (SOP).

The Same-Origin Policy prevents a web page from making requests to a different domain than the one that served the page. Cross-Origin Resource Sharing (CORS) is the mechanism that allows servers to relax this restriction by sending specific HTTP headers.

Key Difference from Standalone Builds

In Unity Editor or standalone builds, your game can freely make HTTP requests to any server. But in WebGL:

  • All network requests go through the browser
  • The browser enforces CORS restrictions
  • Your server must explicitly allow cross-origin requests

Why C# Code is Subject to CORS

You might wonder why CORS—a browser security feature—affects C# code. The answer is that Unity WebGL compiles your C# to WebAssembly (WASM) that runs inside the browser. Under the hood, UnityWebRequest uses the browser’s XMLHttpRequest API to make HTTP calls. The browser doesn’t care that the request originated from C# code—it enforces CORS on all network requests made within its sandbox. This is why the same C# code works fine in standalone builds (no browser) but fails with CORS errors in WebGL builds.

Why Unity WebGL Gets CORS Errors

CORS errors occur when:

  1. Missing Headers: The target server doesn’t include Access-Control-Allow-Origin in its response
  2. Preflight Failures: The server doesn’t handle OPTIONS requests properly
  3. Credential Conflicts: Using withCredentials with a wildcard origin
  4. Method Restrictions: The server doesn’t allow the HTTP method you’re using
  5. Header Restrictions: Custom headers aren’t allowed by the server

Common CORS Error Messages

Here are the typical errors you’ll see in the browser console:

No Access-Control-Allow-Origin Header

Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at https://api.example.com/data.
(Reason: CORS header 'Access-Control-Allow-Origin' missing)

Preflight Request Failed

Access to XMLHttpRequest at 'https://api.example.com/data' from origin
'https://mygame.com' has been blocked by CORS policy: Response to
preflight request doesn't pass access control check.

Credentials Mode Error

The value of the 'Access-Control-Allow-Origin' header in the response
must not be the wildcard '*' when the request's credentials mode is 'include'.

Server-Side Solutions

If you control the server your Unity game is connecting to, the proper fix is to configure CORS headers.

Required Headers

Add these headers to your server’s HTTP responses:

Access-Control-Allow-Origin: https://yourgame.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With

Handling Preflight Requests

Unity’s UnityWebRequest may trigger preflight requests. Your server must respond to OPTIONS requests with the appropriate CORS headers:

Node.js/Express Example:

const express = require('express');
const app = express();

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'https://yourgame.com');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');

  if (req.method === 'OPTIONS') {
    return res.sendStatus(200);
  }
  next();
});

Nginx Configuration:

location /api/ {
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'https://yourgame.com';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
        add_header 'Access-Control-Max-Age' 86400;
        return 204;
    }

    add_header 'Access-Control-Allow-Origin' 'https://yourgame.com';
    proxy_pass http://backend;
}

With Credentials

If your game needs to send cookies or authentication:

Access-Control-Allow-Origin: https://yourgame.com  (cannot be *)
Access-Control-Allow-Credentials: true

Using a CORS Proxy

When you don’t control the target server (third-party APIs, game services, etc.), you need a CORS proxy to add the required headers.

Using corsproxy.io

corsproxy.io is a reliable CORS proxy service that works with Unity WebGL games. Simply prefix your API URL with the proxy:

C# Example:

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class APIManager : MonoBehaviour
{
    private const string CORS_PROXY = "https://corsproxy.io/?url=";

    public IEnumerator FetchData(string apiUrl)
    {
        // Prefix the URL with the CORS proxy
        string proxiedUrl = CORS_PROXY + UnityWebRequest.EscapeURL(apiUrl);

        using (UnityWebRequest request = UnityWebRequest.Get(proxiedUrl))
        {
            yield return request.SendWebRequest();

            if (request.result == UnityWebRequest.Result.Success)
            {
                Debug.Log("Data: " + request.downloadHandler.text);
            }
            else
            {
                Debug.LogError("Error: " + request.error);
            }
        }
    }
}

Conditional Proxy Usage

Use the proxy only in WebGL builds to avoid unnecessary overhead in other platforms:

public static string GetApiUrl(string endpoint)
{
    string baseUrl = "https://api.example.com" + endpoint;

    #if UNITY_WEBGL && !UNITY_EDITOR
        return "https://corsproxy.io/?url=" + UnityWebRequest.EscapeURL(baseUrl);
    #else
        return baseUrl;
    #endif
}

POST Requests with JSON

public IEnumerator PostData(string apiUrl, string jsonData)
{
    #if UNITY_WEBGL && !UNITY_EDITOR
        apiUrl = "https://corsproxy.io/?url=" + UnityWebRequest.EscapeURL(apiUrl);
    #endif

    using (UnityWebRequest request = new UnityWebRequest(apiUrl, "POST"))
    {
        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);
        request.uploadHandler = new UploadHandlerRaw(bodyRaw);
        request.downloadHandler = new DownloadHandlerBuffer();
        request.SetRequestHeader("Content-Type", "application/json");

        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
        {
            ProcessResponse(request.downloadHandler.text);
        }
    }
}

Unity-Specific Configuration

WebGL Template Settings

In your WebGL build settings, ensure your template doesn’t have restrictive Content Security Policy headers that might interfere with CORS requests.

UnityWebRequest vs WWW

Always use UnityWebRequest instead of the deprecated WWW class. It provides better error handling and more control over request headers:

// Don't use this (deprecated)
WWW www = new WWW(url);

// Use this instead
UnityWebRequest request = UnityWebRequest.Get(url);

Handling Binary Data

For downloading assets like textures or audio:

public IEnumerator DownloadTexture(string imageUrl)
{
    #if UNITY_WEBGL && !UNITY_EDITOR
        imageUrl = "https://corsproxy.io/?url=" + UnityWebRequest.EscapeURL(imageUrl);
    #endif

    using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(imageUrl))
    {
        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
        {
            Texture2D texture = DownloadHandlerTexture.GetContent(request);
            // Use the texture
        }
    }
}

Debugging CORS Issues

Browser Developer Tools

  1. Open browser DevTools (F12)
  2. Go to the Network tab
  3. Look for failed requests (red)
  4. Check the Console for CORS error messages
  5. Inspect the response headers of the failed request

Using cURL to Test

Test your server’s CORS configuration from the command line:

# Check response headers
curl -I https://api.example.com/endpoint

# Simulate a preflight request
curl -X OPTIONS -H "Origin: https://yourgame.com" \
     -H "Access-Control-Request-Method: POST" \
     -v https://api.example.com/endpoint

Common Debugging Checklist

  • ✅ Is the server responding to OPTIONS requests?
  • ✅ Are CORS headers present in the response?
  • ✅ Does Access-Control-Allow-Origin match your game’s origin?
  • ✅ Are all required methods listed in Access-Control-Allow-Methods?
  • ✅ Are custom headers listed in Access-Control-Allow-Headers?

Best Practices

1. Avoid Mixed Content

If your game is served over HTTPS, all API requests must also use HTTPS. Mixed content (HTTPS page loading HTTP resources) is blocked by browsers.

2. Handle Errors Gracefully

if (request.result != UnityWebRequest.Result.Success)
{
    if (request.responseCode == 0)
    {
        // Likely a CORS error - no response received
        Debug.LogError("Network error - possible CORS issue");
        ShowPlayerMessage("Unable to connect. Please try again.");
    }
    else
    {
        Debug.LogError($"HTTP Error {request.responseCode}: {request.error}");
    }
}

3. Cache Responses

Reduce unnecessary requests by caching API responses when appropriate:

private Dictionary<string, string> responseCache = new Dictionary<string, string>();

public IEnumerator FetchWithCache(string url, System.Action<string> callback)
{
    if (responseCache.ContainsKey(url))
    {
        callback(responseCache[url]);
        yield break;
    }

    // Make the request and cache the result
    yield return FetchData(url);
    responseCache[url] = lastResponse;
    callback(lastResponse);
}

4. Use Environment-Specific Configuration

[System.Serializable]
public class ApiConfig
{
    public string baseUrl;
    public bool useCorsProxy;
}

public string BuildUrl(string endpoint)
{
    string url = config.baseUrl + endpoint;

    if (config.useCorsProxy)
    {
        url = "https://corsproxy.io/?url=" + UnityWebRequest.EscapeURL(url);
    }

    return url;
}

Conclusion

CORS errors in Unity WebGL are frustrating but solvable. The key is understanding that these are browser security features, not Unity bugs. Your options are:

  1. Configure the server (best for servers you control)
  2. Use a CORS proxy like corsproxy.io (best for third-party APIs)
  3. Self-host a proxy (best for production games with high traffic)

By implementing proper CORS handling, your Unity WebGL game can communicate with any API and provide a seamless experience for players.

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