Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão do .NET 10 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e do .NET Core. Para a versão atual, consulte a versão do .NET 10 deste artigo.
Este artigo explica como habilitar implantações hospedadas Blazor WebAssembly em ambientes que bloqueiam o download e a execução de arquivos DLL (biblioteca de vínculo dinâmico).
Observação
Essa orientação aborda ambientes que impedem os clientes de baixar e executar DLLs. No .NET 8 ou posterior, Blazor usa o formato de arquivo Webcil para resolver esse problema. Para obter mais informações, confira Hospedar e implantar ASP.NET CoreBlazor WebAssembly. O agrupamento de várias partes usando o pacote NuGet experimental descrito por este artigo não tem suporte para Blazor aplicativos no .NET 8 ou posterior. Você pode usar as diretrizes neste artigo para criar seu próprio pacote NuGet de agrupamento de várias partes para .NET 8 ou posterior.
Blazor WebAssembly os aplicativos exigem DLLs (bibliotecas de vínculo dinâmico) para funcionar, mas alguns ambientes impedem que os clientes baixem e executem DLLs. Os produtos de segurança geralmente são capazes de verificar o conteúdo dos arquivos que atravessam a rede e bloqueiam ou quarentenam arquivos DLL. Este artigo descreve uma abordagem para habilitar Blazor WebAssembly aplicativos nesses ambientes, em que um arquivo de pacote de várias partes é criado a partir das DLLs do aplicativo para que as DLLs possam ser baixadas juntas ignorando as restrições de segurança.
Blazor WebAssembly os aplicativos exigem DLLs (bibliotecas de vínculo dinâmico) para funcionar, mas alguns ambientes impedem que os clientes baixem e executem DLLs. Em um subconjunto desses ambientes, alterar a extensão de nome de arquivo de arquivos DLL (.dll) é suficiente para ignorar as restrições de segurança, mas os produtos de segurança geralmente são capazes de verificar o conteúdo dos arquivos que atravessam a rede e bloqueiam ou quarentenam arquivos DLL. Este artigo descreve uma abordagem para habilitar Blazor WebAssembly aplicativos nesses ambientes, em que um arquivo de pacote de várias partes é criado a partir das DLLs do aplicativo para que as DLLs possam ser baixadas juntas ignorando as restrições de segurança.
Um aplicativo hospedado Blazor WebAssembly pode personalizar seus arquivos publicados e empacotar DLLs de aplicativo usando os seguintes recursos:
- Inicializadores JavaScript que permitem personalizar o Blazor processo de inicialização.
- Extensibilidade do MSBuild para transformar a lista de arquivos publicados e definir Blazor Extensões de Publicação. Blazor As Extensões de Publicação são arquivos definidos durante o processo de publicação que fornecem uma representação alternativa para o conjunto de arquivos necessários para executar um aplicativo publicado Blazor WebAssembly . Neste artigo, uma Blazor Extensão de Publicação é criada que produz um pacote de várias partes com todas as DLLs do aplicativo empacotadas em um único arquivo para que as DLLs possam ser baixadas juntas.
A abordagem demonstrada neste artigo serve como um ponto de partida para os desenvolvedores elaborarem suas próprias estratégias e processos de carregamento personalizados.
Aviso
Qualquer abordagem tomada para contornar uma restrição de segurança deve ser cuidadosamente considerada por suas implicações de segurança. Recomendamos explorar ainda mais o assunto com os profissionais de segurança de rede da sua organização antes de adotar a abordagem neste artigo. As alternativas a serem consideradas incluem:
- Habilite dispositivos de segurança e software de segurança para permitir que os clientes de rede baixem e usem os arquivos exatos exigidos por um Blazor WebAssembly aplicativo.
- Alterne do modelo de Blazor WebAssembly hospedagem para o Blazor Server modelo de hospedagem, que mantém todo o código C# do aplicativo no servidor e não requer o download de DLLs para clientes. Blazor Server também oferece a vantagem de manter o código C# privado sem exigir o uso de aplicativos de API Web para privacidade de código C# com Blazor WebAssembly aplicativos.
Pacote NuGet experimental e aplicativo de exemplo
A abordagem descrita neste artigo é usada pelo pacote experimentalMicrosoft.AspNetCore.Components.WebAssembly.MultipartBundle (NuGet.org) para aplicativos direcionados ao .NET 6 ou posterior. O pacote contém destinos MSBuild para personalizar a Blazor saída de publicação e um inicializador JavaScript para usar um carregador de recursos de inicialização personalizado, cada um destes é descrito em detalhes mais adiante neste artigo.
Aviso
Recursos experimentais e de visualização são fornecidos com a finalidade de coletar comentários e não têm suporte para uso em produção.
Posteriormente neste artigo, a seção Personalizar o Blazor WebAssembly processo de carregamento por meio de um pacote NuGet com suas três subseções fornece explicações detalhadas sobre a configuração e o código neste pacote. As explicações detalhadas são importantes para entender quando você cria sua própria estratégia e processo de carregamento personalizado para Blazor WebAssembly aplicativos. Para usar o pacote NuGet publicado, experimental e sem suporte sem personalização como uma demonstração local, execute as seguintes etapas:
Use uma Blazor WebAssemblysolução hospedada existente ou crie uma nova solução a partir do modelo de projeto do Blazor WebAssembly Visual Studio ou passando a opção
-ho|--hostedpara o comandodotnet new(dotnet new blazorwasm -ho). Para obter mais informações, confira Ferramentas para ASP.NET Core Blazor.Client No projeto, adicione o pacote experimental
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Observação
Para obter diretrizes sobre como adicionar pacotes a aplicativos .NET, consulte os artigos em Instalar e gerenciar pacotes no Fluxo de trabalho de consumo de pacotes (documentação do NuGet). Confirme as versões corretas de pacote em NuGet.org.
Server No projeto, adicione um ponto de extremidade para servir o arquivo de pacote (
app.bundle). O código de exemplo pode ser encontrado na seção Servir o pacote da seção de aplicativo do servidor host deste artigo.Publique o aplicativo na Configuração de versão.
Personalizar o Blazor WebAssembly processo de carregamento por meio de um pacote NuGet
Aviso
A orientação nesta seção com suas três subseções refere-se à construção de um pacote NuGet do zero para implementar sua estratégia e processo de carregamento personalizados. O pacote experimentalMicrosoft.AspNetCore.Components.WebAssembly.MultipartBundle (NuGet.org) para .NET 6 e 7 baseia-se nas diretrizes desta seção. Ao usar o pacote fornecido em uma demonstração local da abordagem de download de pacote de várias partes, você não precisa seguir as diretrizes nesta seção. Para obter diretrizes sobre como usar o pacote fornecido, consulte o pacote NuGet experimental e a seção de aplicativo de exemplo .
Blazor os recursos do aplicativo são empacotados em um arquivo de pacote de várias partes e carregados pelo navegador por meio de um inicializador JavaScript (JS) personalizado. Para um aplicativo que consome o pacote com o JS inicializador, o aplicativo requer apenas que o arquivo de pacote seja atendido quando solicitado. Todos os outros aspectos dessa abordagem são tratados de forma transparente.
Quatro personalizações são necessárias para a forma como um aplicativo publicado Blazor padrão carrega:
- Uma tarefa do MSBuild para transformar os arquivos de publicação.
- Um pacote NuGet com alvos do MSBuild que se integra ao Blazor processo de publicação, transforma o resultado e define um ou mais Blazor arquivos de extensão de publicação (nesse caso, um único conjunto).
- Um JS inicializador para atualizar o retorno de chamada do Blazor WebAssembly carregador de recursos para que ele carregue o pacote e forneça ao aplicativo os arquivos individuais.
- Um assistente no aplicativo host Server para garantir que o pacote seja fornecido aos clientes mediante solicitação.
Criar uma tarefa do MSBuild para personalizar a lista de arquivos publicados e definir novas extensões
Crie uma tarefa do MSBuild como uma classe C# pública que pode ser importada como parte de uma compilação do MSBuild e que possa interagir com o build.
O seguinte é necessário para a classe C#:
- Um novo projeto de biblioteca de classes.
- Um framework alvo do projeto de
netstandard2.0. - Referências a pacotes do MSBuild:
Observação
O pacote NuGet para os exemplos deste artigo tem o nome do pacote fornecido pela Microsoft. Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle Para obter diretrizes sobre como nomear e produzir seu próprio pacote NuGet, consulte os seguintes artigos do NuGet:
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks/Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Framework" Version="{VERSION}" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="{VERSION}" />
</ItemGroup>
</Project>
Determine as versões mais recentes dos pacotes para os {VERSION} marcadores de posição no NuGet.org.
Para criar a tarefa MSBuild, crie uma classe C# pública estendendo Microsoft.Build.Utilities.Task (não System.Threading.Tasks.Task) e declare três propriedades:
-
PublishBlazorBootStaticWebAsset: a lista de arquivos a serem publicados para o Blazor aplicativo. -
BundlePath: o caminho onde o pacote é gravado. -
Extension: as novas Extensões de Publicação a serem incluídas no build.
A classe de exemplo BundleBlazorAssets a seguir é um ponto de partida para personalização adicional:
-
ExecuteNo método, o pacote é criado com base nos três tipos de arquivo a seguir:- Arquivos JavaScript (
dotnet.js) - Arquivos WebAssembly (Wasm) (
dotnet.wasm) - DLLs de aplicativo (
.dll)
- Arquivos JavaScript (
- Um
multipart/form-datapacote é criado. Cada arquivo é adicionado ao pacote com suas respectivas descrições por meio do cabeçalho Content-Disposition e do cabeçalho Content-Type. - Depois que o pacote é criado, o pacote é gravado em um arquivo.
- O build está configurado para a extensão. O código a seguir cria um item de extensão e o adiciona à propriedade
Extension. Cada item de extensão contém três partes de dados:- O caminho para o arquivo de extensão.
- O caminho da URL relativo à raiz do aplicativo Blazor WebAssembly.
- O nome da extensão, que agrupa os arquivos produzidos por uma determinada extensão.
Depois de atingir as metas anteriores, a tarefa MSBuild é configurada para ajustar o resultado de Blazor publicação.
Blazor cuida da coleta das extensões e certificação de que as extensões sejam copiadas para o local correto na pasta de saída de publicação (por exemplo, bin\Release\net6.0\publish). As mesmas otimizações (por exemplo, compactação) são aplicadas aos arquivos JavaScript, Wasm e DLL, conforme Blazor se aplica a outros arquivos.
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks/BundleBlazorAssets.cs:
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks
{
public class BundleBlazorAssets : Task
{
[Required]
public ITaskItem[]? PublishBlazorBootStaticWebAsset { get; set; }
[Required]
public string? BundlePath { get; set; }
[Output]
public ITaskItem[]? Extension { get; set; }
public override bool Execute()
{
var bundle = new MultipartFormDataContent(
"--0a7e8441d64b4bf89086b85e59523b7d");
foreach (var asset in PublishBlazorBootStaticWebAsset)
{
var name = Path.GetFileName(asset.GetMetadata("RelativePath"));
var fileContents = File.OpenRead(asset.ItemSpec);
var content = new StreamContent(fileContents);
var disposition = new ContentDispositionHeaderValue("form-data");
disposition.Name = name;
disposition.FileName = name;
content.Headers.ContentDisposition = disposition;
var contentType = Path.GetExtension(name) switch
{
".js" => "text/javascript",
".wasm" => "application/wasm",
_ => "application/octet-stream"
};
content.Headers.ContentType =
MediaTypeHeaderValue.Parse(contentType);
bundle.Add(content);
}
using (var output = File.Open(BundlePath, FileMode.OpenOrCreate))
{
output.SetLength(0);
bundle.CopyToAsync(output).ConfigureAwait(false).GetAwaiter()
.GetResult();
output.Flush(true);
}
var bundleItem = new TaskItem(BundlePath);
bundleItem.SetMetadata("RelativePath", "app.bundle");
bundleItem.SetMetadata("ExtensionName", "multipart");
Extension = new ITaskItem[] { bundleItem };
return true;
}
}
}
Desenvolver um pacote NuGet para transformar automaticamente o resultado da publicação
Gere um pacote NuGet com destinos do MSBuild que são incluídos automaticamente quando o pacote é referenciado:
- Crie um novo projeto de biblioteca de classes (RCL)Razor.
- Crie um arquivo de targets seguindo as convenções do NuGet para importar automaticamente o pacote em projetos que consomem. Por exemplo, crie
build\net6.0\{PACKAGE ID}.targets, onde{PACKAGE ID}está o identificador de pacote do pacote. - Colete a saída da biblioteca de classes que contém a tarefa MSBuild e confirme se a saída está armazenada no local certo.
- Adicione o código MSBuild necessário para anexar ao Blazor pipeline e invocar a tarefa MSBuild para gerar o pacote.
A abordagem descrita nesta seção usa apenas o pacote para fornecer destinos e conteúdo, que é diferente da maioria dos pacotes em que o pacote inclui uma DLL de biblioteca.
Aviso
O pacote de exemplo descrito nesta seção demonstra como personalizar o Blazor processo de publicação. O pacote NuGet de exemplo é usado apenas como uma demonstração local. Não há suporte para o uso desse pacote em produção.
Observação
O pacote NuGet para os exemplos deste artigo tem o nome do pacote fornecido pela Microsoft. Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle Para obter diretrizes sobre como nomear e produzir seu próprio pacote NuGet, consulte os seguintes artigos do NuGet:
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle/Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.csproj:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<NoWarn>NU5100</NoWarn>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Description>
Sample demonstration package showing how to customize the Blazor publish
process. Using this package in production is not supported!
</Description>
<IsPackable>true</IsPackable>
<IsShipping>true</IsShipping>
<IncludeBuildOutput>false</IncludeBuildOutput>
</PropertyGroup>
<ItemGroup>
<None Update="build\**"
Pack="true"
PackagePath="%(Identity)" />
<Content Include="_._"
Pack="true"
PackagePath="lib\net6.0\_._" />
</ItemGroup>
<Target Name="GetTasksOutputDlls"
BeforeTargets="CoreCompile">
<MSBuild Projects="..\Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks\Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks.csproj"
Targets="Publish;PublishItemsOutputGroup"
Properties="Configuration=Release">
<Output TaskParameter="TargetOutputs"
ItemName="_TasksProjectOutputs" />
</MSBuild>
<ItemGroup>
<Content Include="@(_TasksProjectOutputs)"
Condition="'%(_TasksProjectOutputs.Extension)' == '.dll'"
Pack="true"
PackagePath="tasks\%(_TasksProjectOutputs.TargetPath)"
KeepMetadata="Pack;PackagePath" />
</ItemGroup>
</Target>
</Project>
Observação
A propriedade <NoWarn>NU5100</NoWarn> no exemplo anterior suprime o aviso sobre os assemblies colocados no diretório tasks. Para obter mais informações, consulte NuGet Warning NU5100.
Adicione o arquivo .targets para conectar a tarefa do MSBuild ao pipeline de construção. Neste arquivo, as seguintes metas são realizadas:
- Importe a tarefa para o processo de build. Observe que o caminho para a DLL é relativo ao local final do arquivo no pacote.
- A
ComputeBlazorExtensionsDependsOnpropriedade anexa o destino personalizado ao Blazor WebAssembly pipeline. - Capturar a propriedade
Extensionna saída da tarefa e adicioná-la aBlazorPublishExtensionpara informar Blazor sobre a extensão. Invocar a tarefa na meta produz o pacote. A lista de arquivos publicados é fornecida pelo Blazor WebAssembly pipeline noPublishBlazorBootStaticWebAssetgrupo de itens. O caminho do pacote é definido usando oIntermediateOutputPath(normalmente dentro daobjpasta). Por fim, o pacote é copiado automaticamente para o local correto na pasta de saída de publicação (por exemplo,bin\Release\net6.0\publish).
Quando o pacote é referenciado, ele gera um conjunto dos arquivos Blazor durante a publicação.
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle/build/net6.0/Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.targets:
<Project>
<UsingTask
TaskName="Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks.BundleBlazorAssets"
AssemblyFile="$(MSBuildThisProjectFileDirectory)..\..\tasks\Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.Tasks.dll" />
<PropertyGroup>
<ComputeBlazorExtensionsDependsOn>
$(ComputeBlazorExtensionsDependsOn);_BundleBlazorDlls
</ComputeBlazorExtensionsDependsOn>
</PropertyGroup>
<Target Name="_BundleBlazorDlls">
<BundleBlazorAssets
PublishBlazorBootStaticWebAsset="@(PublishBlazorBootStaticWebAsset)"
BundlePath="$(IntermediateOutputPath)bundle.multipart">
<Output TaskParameter="Extension"
ItemName="BlazorPublishExtension"/>
</BundleBlazorAssets>
</Target>
</Project>
Inicializar automaticamente Blazor do pacote
O pacote NuGet utiliza inicializadores JavaScript (JS) para inicializar automaticamente um Blazor WebAssembly aplicativo do pacote em vez de usar arquivos DLL individuais. JS inicializadores são usados para alterar o Blazorcarregador de recursos de inicialização e utilizar o pacote.
Para criar um JS inicializador, adicione um JS arquivo com o nome {NAME}.lib.module.js à wwwroot pasta do projeto do pacote, em que o {NAME} placeholder é o identificador do pacote. Por exemplo, o arquivo para o pacote da Microsoft é nomeado Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.lib.module.js. As funções exportadas beforeWebAssemblyStart e afterWebAssemblyStarted lidam com o carregamento.
Os JS inicializadores:
- Detecte se a Extensão de Publicação está disponível verificando se
extensions.multipart, que é o nome da extensão (ExtensionName) fornecido na seção Criar uma tarefa MSBuild para personalizar a lista de arquivos publicados e definir novas extensões. - Baixe o pacote e analise o conteúdo em um mapa de recursos usando URLs de objeto geradas.
- Atualize o carregador de recursos de inicialização (
options.loadBootResource) com uma função personalizada que resolve os recursos usando as URLs do objeto. - Após o aplicativo ser iniciado, libere a memória revogando as URLs do objeto na função
afterWebAssemblyStarted.
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle/wwwroot/Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.lib.module.js:
const resources = new Map();
export async function beforeWebAssemblyStart(options, extensions) {
if (!extensions || !extensions.multipart) {
return;
}
try {
const integrity = extensions.multipart['app.bundle'];
const bundleResponse =
await fetch('app.bundle', { integrity: integrity, cache: 'no-cache' });
const bundleFromData = await bundleResponse.formData();
for (let value of bundleFromData.values()) {
resources.set(value, URL.createObjectURL(value));
}
options.loadBootResource = function (type, name, defaultUri, integrity) {
return resources.get(name) ?? null;
}
} catch (error) {
console.log(error);
}
}
export async function afterWebAssemblyStarted(blazor) {
for (const [_, url] of resources) {
URL.revokeObjectURL(url);
}
}
Para criar um JS inicializador, adicione um JS arquivo com o nome {NAME}.lib.module.js à wwwroot pasta do projeto do pacote, em que o {NAME} placeholder é o identificador do pacote. Por exemplo, o arquivo para o pacote da Microsoft é nomeado Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.lib.module.js. As funções exportadas beforeStart e afterStarted lidam com o carregamento.
Os JS inicializadores:
- Detecte se a Extensão de Publicação está disponível verificando se
extensions.multipart, que é o nome da extensão (ExtensionName) fornecido na seção Criar uma tarefa MSBuild para personalizar a lista de arquivos publicados e definir novas extensões. - Baixe o pacote e analise o conteúdo em um mapa de recursos usando URLs de objeto geradas.
- Atualize o carregador de recursos de inicialização (
options.loadBootResource) com uma função personalizada que resolve os recursos usando as URLs do objeto. - Após o aplicativo ser iniciado, libere a memória revogando as URLs do objeto na função
afterStarted.
Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle/wwwroot/Microsoft.AspNetCore.Components.WebAssembly.MultipartBundle.lib.module.js:
const resources = new Map();
export async function beforeStart(options, extensions) {
if (!extensions || !extensions.multipart) {
return;
}
try {
const integrity = extensions.multipart['app.bundle'];
const bundleResponse =
await fetch('app.bundle', { integrity: integrity, cache: 'no-cache' });
const bundleFromData = await bundleResponse.formData();
for (let value of bundleFromData.values()) {
resources.set(value, URL.createObjectURL(value));
}
options.loadBootResource = function (type, name, defaultUri, integrity) {
return resources.get(name) ?? null;
}
} catch (error) {
console.log(error);
}
}
export async function afterStarted(blazor) {
for (const [_, url] of resources) {
URL.revokeObjectURL(url);
}
}
Servir o pacote do aplicativo do servidor host
Devido a restrições de segurança, o ASP.NET Core não atende ao app.bundle arquivo. Um auxiliar de processamento de solicitação é necessário para atender ao arquivo quando ele é solicitado pelos clientes.
Observação
Como as mesmas otimizações são aplicadas de forma transparente às Extensões de Publicação que são aplicadas aos arquivos do aplicativo, os arquivos de ativos compactados app.bundle.gz e app.bundle.br são produzidos automaticamente ao publicar.
Coloque o código C# no projeto Program.cs imediatamente antes da linha que define o arquivo de fallback como Server (index.html), para responder a uma solicitação para o arquivo de pacote, por exemplo, app.MapFallbackToFile("index.html");:
app.MapGet("app.bundle", (HttpContext context) =>
{
string? contentEncoding = null;
var contentType =
"multipart/form-data; boundary=\"--0a7e8441d64b4bf89086b85e59523b7d\"";
var fileName = "app.bundle";
var acceptEncodings = context.Request.Headers.AcceptEncoding;
if (Microsoft.Net.Http.Headers.StringWithQualityHeaderValue
.StringWithQualityHeaderValue
.TryParseList(acceptEncodings, out var encodings))
{
if (encodings.Any(e => e.Value == "br"))
{
contentEncoding = "br";
fileName += ".br";
}
else if (encodings.Any(e => e.Value == "gzip"))
{
contentEncoding = "gzip";
fileName += ".gz";
}
}
if (contentEncoding != null)
{
context.Response.Headers.ContentEncoding = contentEncoding;
}
return Results.File(
app.Environment.WebRootFileProvider.GetFileInfo(fileName)
.CreateReadStream(), contentType);
});
O tipo de conteúdo corresponde ao tipo definido anteriormente na tarefa de build. O endpoint verifica as codificações de conteúdo aceitas pelo navegador e serve o arquivo ideal, Brotli (.br) ou Gzip (.gz).