Hi all,
I’m hosting a Unity WebGL build of Drift Hunters on NGINX (DriftHuntersPlus). Files are precompressed by Unity as Brotli (.data.br, .wasm.br, .js.br). I’m trying to serve them with the correct Content-Type and Content-Encoding and enable cross-origin isolation for multithreading/SIMD.
Environment
-
NGINX OSS 1.24.x (no dynamic brotli module)
-
Ubuntu 22.04
-
Unity 2022 LTS WebGL build
Files:Build/DriftHunters.data.br,Build/DriftHunters.framework.js.br,Build/DriftHunters.wasm.br,Build/DriftHunters.loader.js
Symptoms
-
Browser console shows either:
-
TypeError: Failed to execute 'compile' on 'WebAssembly': Incorrect response MIME type. Expected 'application/wasm'. -
or
Uncaught (in promise) TypeError: Failed to decompress data/ERR_CONTENT_DECODING_FAILED
-
-
If I enable COOP/COEP, some assets are blocked with CORP unless I add headers.
Goal
-
Serve .br files with correct Content-Type and Content-Encoding: br
-
Fallback to .gz (if I rebuild with gzip) using
gzip_static on; -
Set COOP/COEP + CORP for isolation
-
Add long-cache for large assets
Current NGINX config (simplified)
server {
listen 80;
server_name game.example.com;
root /var/www/drift;
# Cross-origin isolation for WebGL multithreading/SIMD
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
# Default index
location = / { try_files /index.html =404; }
location = /index.html {
# short cache to allow quick updates
add_header Cache-Control "no-cache";
}
# Serve Unity precompressed Brotli files (.br)
location ~* ^/Build/(.+)\.data\.br$ {
types { application/octet-stream data; } # or leave default
default_type application/octet-stream;
add_header Content-Encoding br;
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Cross-Origin-Resource-Policy "same-origin";
try_files $uri =404;
}
location ~* ^/Build/(.+)\.wasm\.br$ {
default_type application/wasm;
add_header Content-Encoding br;
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Cross-Origin-Resource-Policy "same-origin";
try_files $uri =404;
}
location ~* ^/Build/(.+)\.js\.br$ {
default_type application/javascript;
add_header Content-Encoding br;
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Cross-Origin-Resource-Policy "same-origin";
try_files $uri =404;
}
# If I rebuild with gzip instead of brotli:
gzip_static on;
location ~* ^/Build/(.+)\.(data|wasm|js)\.gz$ {
# Unity emits .gz files; serve with the base MIME and Content-Encoding gzip
set $ext $2;
if ($ext = wasm) { set $ct application/wasm; }
if ($ext = js) { set $ct application/javascript; }
if ($ext = data) { set $ct application/octet-stream; }
default_type $ct;
add_header Content-Encoding gzip;
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Cross-Origin-Resource-Policy "same-origin";
try_files $uri =404;
}
# Static assets (images, StreamingAssets)
location / {
try_files $uri $uri/ =404;
}
}
Questions
-
Is the approach above the recommended way to serve Unity’s precompressed .br assets when the brotli module is not enabled (i.e., just set
Content-Encoding: bron static files)? -
For .wasm.br, is
default_type application/wasm+Content-Encoding: brsufficient for all modern browsers? -
Any better way to avoid the
Incorrect response MIME typeerror without relying onifblocks (e.g., usingmapfor content types)? -
Are these COOP/COEP/CORP headers correct for Unity WebGL multithreading? Anything else needed (e.g., COEP credentialless)?
-
For caching: is
Cache-Control: public, max-age=31536000, immutablesafe for.data/.wasm/.jsas long as filenames are content-hashed?
If anyone has a canonical NGINX snippet for Unity WebGL (Brotli and gzip fallbacks), I’d love to see it. Thanks!
— Illia / DriftHuntersPlus