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
- Understanding CORS in Unity WebGL
- Why Unity WebGL Gets CORS Errors
- Common CORS Error Messages
- Server-Side Solutions
- Using a CORS Proxy
- Unity-Specific Configuration
- Debugging CORS Issues
- 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,
UnityWebRequestuses the browser’sXMLHttpRequestAPI 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:
- Missing Headers: The target server doesn’t include
Access-Control-Allow-Originin its response - Preflight Failures: The server doesn’t handle
OPTIONSrequests properly - Credential Conflicts: Using
withCredentialswith a wildcard origin - Method Restrictions: The server doesn’t allow the HTTP method you’re using
- 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
- Open browser DevTools (F12)
- Go to the Network tab
- Look for failed requests (red)
- Check the Console for CORS error messages
- 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-Originmatch 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:
- Configure the server (best for servers you control)
- Use a CORS proxy like corsproxy.io (best for third-party APIs)
- 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.