Erros do WebGL na Unity: Compressão, Gzip e Tela Preta

Unity WebGL compression error por sintoma: Unable to parse Build .gz, tela preta, carga parada em 90%, Content-Encoding no servidor e Decompression Fallback.
Erros do WebGL na Unity: Compressão, Gzip e Tela Preta
O unity webgl compression error mais comum, "Unable to parse Build/...framework.js.gz", quase nunca é bug da build: é o servidor entregando um arquivo comprimido sem o cabeçalho Content-Encoding que diz ao navegador como descomprimir. Você resolve de dois jeitos: configurar o servidor (melhor) ou ativar Decompression Fallback na Unity (quando não dá para mexer no servidor).
Este post organiza os erros da build Web por sintoma, com a causa, o teste que confirma e a correção para cada um. Inclui a configuração de Nginx e Apache baseada nos exemplos oficiais da Unity e um servidor local em Python para testar sem cair no erro de file://.
Como a compressão da build Web funciona
Quando você gera a build Web (plataforma Web em File > Build Profiles), a Unity comprime os três arquivos grandes da pasta Build/:
.data: cenas, assets e recursos..framework.js: o JavaScript de ligação com o navegador..wasm: o seu código C# convertido em WebAssembly.
Com gzip, eles saem como Web.data.gz, Web.framework.js.gz e Web.wasm.gz. O Web.loader.js não é comprimido: ele é quem baixa e liga o resto.
A ideia é que o navegador descomprima durante o download, o que é rápido e não custa nada ao jogo. Para isso, o servidor precisa responder cada .gz com Content-Encoding: gzip (ou br para Brotli). Sem o cabeçalho, o navegador entrega os bytes comprimidos ao loader, que não consegue lê-los.
Um detalhe que confunde: segundo o manual do Unity 6.3, Compression Format vale só para builds de release. Development Build não é comprimida. Por isso "a build de desenvolvimento funcionava e a final não".
Unity WebGL compression error: a mensagem e a causa
A mensagem que aparece na página, para gzip, é esta:
Unable to parse Build/Web.framework.js.gz! This can happen if build compression was enabled but web server hosting the content was misconfigured to not serve the file with HTTP Response Header "Content-Encoding: gzip" present. Check browser Console and Devtools Network tab to debug.
O nome do arquivo muda conforme a sua build. Para Brotli, a mensagem fala em Content-Encoding: br e acrescenta que a compressão Brotli pode não ser suportada em conexões HTTP.
Como confirmar em 30 segundos:
- Abra o jogo no navegador e pressione F12.
- Aba Network (Rede), recarregue a página.
- Clique no arquivo
.framework.js.gze veja Response Headers. - Se não existe
Content-Encoding: gzip, a causa é essa.
Gzip vs Brotli vs Disabled
Em Edit > Project Settings > Player > Web > Publishing Settings > Compression Format, o manual do Unity 6.3 descreve as três opções:
- Gzip (padrão): arquivos maiores que Brotli, mas build mais rápida e suporte nativo em todos os navegadores, em HTTP e HTTPS. É a escolha segura.
- Brotli: a melhor taxa de compressão, porém build mais lenta, e Chrome e Firefox só descomprimem Brotli nativamente via HTTPS.
- Disabled: sem compressão. Use quando o servidor ou a CDN já comprime os arquivos por conta própria, ou para testes locais.
Regra prática: hospedagem sem HTTPS ou que você não conhece bem, gzip. Servidor seu, com HTTPS e cabeçalhos configurados, Brotli economiza download.
Decompression Fallback: prós e contras
Com Decompression Fallback ligado (mesma seção Publishing Settings), a Unity embute um descompressor em JavaScript na build e os arquivos passam a ter a extensão .unityweb. Se o navegador não descomprimir, o loader descomprime.
A favor: a build funciona em quase qualquer hospedagem estática, sem configurar nada.
Contra, segundo o manual: loader maior e esquema de carregamento menos eficiente. E a compilação do WebAssembly em streaming (o navegador compilar enquanto baixa) deixa de funcionar, porque o .wasm precisa passar pelo descompressor primeiro. Em jogo grande, isso aumenta o tempo até a primeira tela.
Para alternar entre perfis de build sem lembrar de cada caixa, use um script de editor. Arquivo Assets/Editor/WebCompressionMenu.cs:
using UnityEditor;
public static class WebCompressionMenu
{
[MenuItem("Tools/Web/Servidor configurado (Gzip nativo)")]
public static void UseNativeGzip()
{
PlayerSettings.WebGL.compressionFormat = WebGLCompressionFormat.Gzip;
PlayerSettings.WebGL.decompressionFallback = false;
}
[MenuItem("Tools/Web/Hospedagem sem controle (Fallback)")]
public static void UseFallback()
{
PlayerSettings.WebGL.compressionFormat = WebGLCompressionFormat.Gzip;
PlayerSettings.WebGL.decompressionFallback = true;
}
[MenuItem("Tools/Web/Sem compressão (teste local)")]
public static void UseNoCompression()
{
PlayerSettings.WebGL.compressionFormat = WebGLCompressionFormat.Disabled;
PlayerSettings.WebGL.decompressionFallback = false;
}
}
A API ainda usa o nome WebGL em PlayerSettings.WebGL, mesmo com a plataforma chamada Web no Unity 6.
Configurar servidor: Content-Encoding e Content-Type
O manual define dois cabeçalhos por arquivo. Content-Encoding (gzip para .gz, br para .br) e Content-Type: application/wasm para .wasm (habilita a compilação em streaming) e application/javascript para .js.
Nginx, baseado no exemplo oficial da Unity para gzip, dentro do location do site:
location ~ .+\.(data|symbols\.json)\.gz$ {
gzip off;
add_header Content-Encoding gzip;
default_type application/gzip;
}
location ~ .+\.js\.gz$ {
gzip off;
add_header Content-Encoding gzip;
default_type application/javascript;
}
location ~ .+\.wasm\.gz$ {
gzip off;
add_header Content-Encoding gzip;
default_type application/wasm;
}
O gzip off evita que o Nginx tente comprimir de novo um arquivo que já está comprimido. O tipo application/gzip no .data é o que a Unity recomenda por causa de um bug do Safari.
Apache com .htaccess (exige o módulo mod_mime), também do exemplo oficial:
<IfModule mod_mime.c>
RemoveType .gz
AddEncoding gzip .gz
AddType application/gzip .data.gz
AddType application/wasm .wasm.gz
AddType application/javascript .js.gz
AddType application/octet-stream .symbols.json.gz
</IfModule>
Para Brotli, troque .gz por .br e gzip por br nos dois casos, e lembre do HTTPS.
Hospedagens estáticas (GitHub Pages e similares) em geral não deixam definir cabeçalho por arquivo. Nelas, use Decompression Fallback ou Compression Format em Disabled. No itch.io, a documentação de HTML5 informa que arquivos .br e gzip são detectados e servidos com o cabeçalho certo, então a build padrão funciona sem fallback. O passo a passo da página está em publicar Unity WebGL no itch.io.
Corrigir servidor é a parte mecânica. A parte que pesa é quando o jogo carrega e mesmo assim não funciona direito: tela preta por script que quebra na inicialização, memória estourada, cena pesada demais para o navegador. Esses problemas nascem na arquitetura do jogo, e é aí que aprender a estruturar um projeto completo faz diferença.
Tela preta e travar em 90%
Tela preta depois da barra encher: os arquivos carregaram, mas algo falhou ao iniciar.
- Abra o Console do navegador (F12) e procure a primeira exceção vermelha. Exceção de C# aparece com o nome do script quando a build tem stack trace.
- Gere uma Development Build: ela não é comprimida (descarta compressão como causa) e mostra mensagens mais claras.
- Causas frequentes: script que usa
System.IO.Fileou thread (não suportados no navegador), shader não incluído na build, cena inicial errada na Scene List.
Carregamento parado em 90% ou perto do fim: o download terminou e a página está compilando o WebAssembly ou inicializando.
- Em build grande, isso é lentidão, não erro. Espere um minuto e veja se avança.
- Se o servidor não envia
Content-Type: application/wasm, o navegador não compila em streaming e a etapa final demora mais. Confira o cabeçalho na aba Network. - Se trava sempre e o console mostra erro de memória (
Out of memory), a cena é pesada demais para o navegador. Reduza texturas e divida o conteúdo.
Testar localmente sem erro de file://
Abrir o index.html com duplo clique usa o protocolo file://, e o navegador bloqueia o carregamento dos arquivos da build. Três saídas:
- Build And Run em Build Profiles: a Unity sobe um servidor local já configurado.
- Build sem compressão (menu do script acima) e qualquer servidor estático.
- Um servidor local que envia os cabeçalhos certos. Salve como
servidor_unity.pydentro da pasta da build e rodepython servidor_unity.py:
import http.server
class UnityHandler(http.server.SimpleHTTPRequestHandler):
def end_headers(self):
path = self.path.split("?")[0]
if path.endswith(".gz"):
self.send_header("Content-Encoding", "gzip")
elif path.endswith(".br"):
self.send_header("Content-Encoding", "br")
super().end_headers()
def guess_type(self, path):
name = str(path)
for ext in (".gz", ".br"):
if name.endswith(ext):
name = name[: -len(ext)]
if name.endswith(".wasm"):
return "application/wasm"
if name.endswith(".js"):
return "application/javascript"
if name.endswith(".data"):
return "application/octet-stream"
return super().guess_type(path)
if __name__ == "__main__":
print("Abra http://localhost:8000")
http.server.ThreadingHTTPServer(("localhost", 8000), UnityHandler).serve_forever()
Ele só usa a biblioteca padrão do Python. Teste com gzip: Brotli pode não ser aceito em HTTP pelo navegador, como visto acima. Esse servidor é para teste na sua máquina, não para hospedar o jogo.
Checklist de diagnóstico
- A mensagem cita
Content-Encoding? Confira os cabeçalhos na aba Network. - Está usando Brotli? O site está em HTTPS? Se não, troque para gzip.
- Pode mexer no servidor? Configure
Content-EncodingeContent-Type. Não pode? Ative Decompression Fallback. - A hospedagem ou CDN comprime por conta própria? Use Compression Format em Disabled para não comprimir duas vezes.
- Carrega e fica preto? Console do navegador e Development Build.
- Para em 90%? Espere, confira
application/wasme memória. - Abriu por
file://? Use Build And Run ou um servidor local.
Próximo passo
Gere duas builds do mesmo projeto, uma com gzip nativo e outra com fallback, e suba as duas no seu servidor. Compare o tempo até a primeira tela: você vai ver na prática o custo do fallback e decidir com número. Se o destino é o itch.io, a configuração padrão já resolve; leia como publicar jogo no itch.io para montar a página. E se em outro projeto você usa Godot, os mesmos cabeçalhos aparecem em exportar jogo Godot para web.
Perguntas frequentes
O que significa "Unable to parse Build/...gz" na Unity?
Significa que o navegador recebeu o arquivo comprimido sem o cabeçalho Content-Encoding que avisa que ele é gzip ou Brotli. O loader da Unity tenta ler bytes comprimidos como se fossem JavaScript e falha. A correção é configurar o servidor ou ativar Decompression Fallback na build.
Brotli funciona em HTTP?
Não de forma nativa no Chrome e no Firefox. O manual do Unity 6.3 informa que esses navegadores só descomprimem Brotli nativamente via HTTPS. Em hospedagem sem HTTPS, use gzip, que funciona em HTTP e HTTPS em todos os navegadores.
Devo ativar Decompression Fallback?
Ative quando você não controla o servidor, por exemplo numa hospedagem que não deixa mudar cabeçalhos. O custo é um loader maior e um carregamento menos eficiente, e o WebAssembly perde a compilação em streaming. Quando você pode configurar o servidor, deixe desligado.
Por que a build abre no Build And Run e não abre no meu servidor?
Porque o servidor local da Unity já envia os cabeçalhos certos para .gz e .br, e o seu servidor não. Compare os cabeçalhos de resposta dos arquivos da pasta Build na aba Network do navegador nos dois casos e copie a configuração.


