O cabeçalho de resposta HTTP Connection-Allowlist restringe um documento ou worker para que ele se comunique apenas com uma lista explícita de endpoints de rede. Ao contrário da Política de Segurança de Conteúdo (CSP), que exige a manutenção de diretivas separadas por recurso (connect-src, img-src, script-src, font-src, form-action) e não controla canais de rede especulativos ou que não são de busca, como <link rel="dns-prefetch">, <link rel="preconnect"> ou WebRTC, a Connection-Allowlist aplica uma única lista de permissões na camada de rede em todas as conexões de saída iniciadas pelo contexto (fetch(), XMLHttpRequest, subrecursos, navigator.sendBeacon(), <a ping>, navegações de nível superior e de frame, dicas de recursos, regras de especulação, WebSocket, WebTransport e WebRTC).
Como fazer a implementação
- Serialize a lista de permissões como uma lista interna do RFC 9651
(...): formateConnection-Allowlist(ouConnection-Allowlist-Report-Only) como uma única lista entre parênteses de itens separados por espaços. Inclua o token sem aspasresponse-originpara permitir a origem do documento ou do worker, seguido por stringsURLPatternentre aspas duplas para cada endpoint externo legítimo ao qual o contexto se conecta ou navega (APIs, CDNs de imagem/fonte e destinos de redirecionamento de pagamento, OAuth,<a href>ou<form action>de saída). Ao criar padrões dinamicamente com base em objetos de configuração ouURL, normalizews:/wss:parahttp:/https:e remova as barras finais em URLs somente de origem (porquenew URL('https://api.example.com').hrefanexa/, o que restringe a correspondência deURLPatternapenas ao caminho raiz/). - Defina o escopo do middleware por
Sec-Fetch-Deste cubra os workers: os navegadores só respeitamConnection-Allowlistem respostas de documentos/navegação e de scripts de worker (valoresSec-Fetch-Destdocument,iframe,worker,sharedworkereserviceworker). Ao variar os cabeçalhos porSec-Fetch-Dest, incluaVary: Sec-Fetch-Destpara que os caches compartilhados não mostrem respostas sem cabeçalho. Disponibilize uma lista de permissões correspondente em scripts de worker dedicado e compartilhado para que scripts de página comprometidos não possam exfiltrar dados por meio deworker.postMessage(). Como um service worker realiza as próprias buscas de rede em ausências no cache e pré-carregamentos de navegação, configure oConnection-Allowlistdele como um superconjunto das listas de permissões de todos os documentos que ele controla. - Configurar relatórios de violação e lançamento em etapas ou duplo: defina um cabeçalho
Reporting-Endpointse anexe um parâmetro; report-to=<endpoint-name>sem aspas após o)de fechamento. É possível enviar apenasConnection-Allowlist-Report-Onlydurante o lançamento inicial ou enviar ambosConnection-Allowlist(aplicando uma lista de permissões de referência) eConnection-Allowlist-Report-Only(auditando uma lista de permissões de candidatos mais rigorosa) na mesma resposta. Os relatórios de violação são enviados com"type": "connection-allowlist"e corpo{ url, connection, allowlist, disposition }(em queconnectioné o URL de destino bloqueado ou"webrtc", edispositioné"enforce"ou"report"). - Considere o redirecionamento padrão e o bloqueio do WebRTC e lide com erros do cliente: por padrão, o
Connection-Allowlistbloqueia todos os redirecionamentos HTTP3xx(redirects=block, mesmo ao redirecionar entre URLs na lista de permissões ou de mesma origem) e todo o tráfegoRTCPeerConnection(webrtc=block). Adicione; redirects=allowsomente quando os endpoints confiáveis exigirem redirecionamentos do lado do servidor (a solicitação inicial ainda precisa corresponder à lista de permissões) ou; webrtc=allowquando o WebRTC for necessário. No cliente, envolva as chamadas de saídafetch()emtry...catche processe os eventosWebSocketerrorpara que as solicitações rejeitadas comTypeError(net::ERR_NETWORK_ACCESS_REVOKED) retornem um estado de erro processado e atualizem o status da interface em vez de causar rejeições não processadas.
Exemplo de código
Vary: Sec-Fetch-Dest
Reporting-Endpoints: conn-reports="https://app.example.com/api/security-reports"
Connection-Allowlist: (response-origin "https://api.example.com" "https://realtime.example.com" "https://*.cdn.example.com/*"); report-to=conn-reports
Connection-Allowlist-Report-Only: (response-origin "https://api.example.com/v2/*"); report-to=conn-reports
const ALLOWLIST_DESTS = new Set(['document', 'iframe', 'worker', 'sharedworker', 'serviceworker']);
// Normalize a config URL or URLPattern string for Connection-Allowlist:
// - Rewrites ws:// and wss:// to http:// and https:// (WebSocket matching rule).
// - Strips a bare trailing slash on origin-only URLs (e.g. from new URL(...).href)
// so all subpaths on the origin are permitted instead of only "/".
export function normalizeAllowlistPattern(rawPattern) {
const trimmed = String(rawPattern).trim().replace(/^ws(s?):\/\//i, 'http$1://');
if (!/^https?:\/\//i.test(trimmed)) {
throw new Error(`Pattern must include an http:// or https:// scheme: ${rawPattern}`);
}
if (/[\s,]/.test(trimmed)) {
throw new Error(`Pattern must be a single URLPattern without spaces or commas: ${rawPattern}`);
}
if (/[()]/.test(trimmed)) {
throw new Error(`Custom regex groups are rejected by Connection-Allowlist: ${rawPattern}`);
}
return trimmed.replace(/^(https?:\/\/[^/?#]+)\/$/i, '$1');
}
// Build an RFC 9651 Inner List value for Connection-Allowlist or Connection-Allowlist-Report-Only.
export function buildConnectionAllowlistHeader({
allowSelf = true,
patterns = [],
reportTo,
redirects = 'block',
webrtc = 'block',
} = {}) {
const items = allowSelf ? ['response-origin'] : [];
for (const pattern of patterns) {
const raw = String(pattern).trim();
if (raw === 'response-origin') {
if (!items.includes('response-origin')) items.push('response-origin');
continue;
}
const normalized = normalizeAllowlistPattern(raw);
items.push(`"${normalized.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`);
}
let header = `(${items.join(' ')})`;
if (redirects === 'allow') header += '; redirects=allow';
if (webrtc === 'allow') header += '; webrtc=allow';
if (reportTo) {
const endpointToken = String(reportTo).trim().replace(/^"+|"+$/g, '');
header += `; report-to=${endpointToken}`;
}
return header;
}
// Server or middleware helper applying Connection-Allowlist and Reporting-Endpoints
// on document and worker responses.
export function setConnectionAllowlistHeaders(res, options = {}) {
const {
secFetchDest = 'document',
patterns = ['https://api.example.com', 'https://realtime.example.com', 'https://*.cdn.example.com/*'],
reportOnlyPatterns,
reportEndpointName = 'conn-reports',
reportEndpointUrl = 'https://app.example.com/api/security-reports',
redirects = 'block',
webrtc = 'block',
reportOnly = false,
} = options;
const setHeader = (name, value) =>
typeof res.setHeader === 'function' ? res.setHeader(name, value) : res.headers.set(name, value);
if (typeof res.appendHeader === 'function') {
res.appendHeader('Vary', 'Sec-Fetch-Dest');
} else if (typeof res.headers?.append === 'function') {
res.headers.append('Vary', 'Sec-Fetch-Dest');
} else {
setHeader('Vary', 'Sec-Fetch-Dest');
}
if (secFetchDest && !ALLOWLIST_DESTS.has(String(secFetchDest).toLowerCase())) return;
setHeader('Reporting-Endpoints', `${reportEndpointName}="${reportEndpointUrl}"`);
setHeader(
reportOnly ? 'Connection-Allowlist-Report-Only' : 'Connection-Allowlist',
buildConnectionAllowlistHeader({ allowSelf: true, patterns, reportTo: reportEndpointName, redirects, webrtc }),
);
if (!reportOnly && Array.isArray(reportOnlyPatterns)) {
setHeader(
'Connection-Allowlist-Report-Only',
buildConnectionAllowlistHeader({
allowSelf: true,
patterns: reportOnlyPatterns,
reportTo: reportEndpointName,
redirects,
webrtc,
}),
);
}
}
// Client-side helper that catches blocked outbound requests gracefully without unhandled rejections.
export async function fetchWithPolicyHandling(url, { statusElement, ...fetchOptions } = {}) {
try {
const response = await fetch(url, fetchOptions);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const body = await response.text();
if (statusElement) {
statusElement.textContent = `Allowed: Loaded ${url} (${body.length} bytes).`;
}
return { ok: true, body, error: null };
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (statusElement) {
statusElement.textContent = `Blocked: Request to ${url} was rejected by the outbound connection policy (${message}).`;
}
return { ok: false, body: null, error };
}
}
Práticas recomendadas
- FAÇA a formatação de
Connection-Allowlistcomo uma única lista interna RFC 9651(...)de itens separados por espaços. Se um cabeçalhoConnection-Allowlistnão vazio falhar na análise de campo estruturado de nível superior (por exemplo, omitindo(...)ou usando aspas simples'...'), o navegador vai falhar com uma lista de permissões vazia e bloquear todas as conexões de rede de saída. UseConnection-Allowlist: ()quando quiser revogar intencionalmente todo o acesso à rede após o carregamento (os esquemas locaisdata:,blob:,about:efilesystem:permanecem permitidos). - OMITA a barra final em padrões de URL de toda a origem (
"https://api.example.com") ou use um caractere curinga explícito ("https://api.example.com/*") para que subcaminhos como/v1/userssejam permitidos. Remova as barras finais adicionadas pornew URL(origin).hrefantes de serializar os padrões. - É RECOMENDÁVEL colocar na lista de permissões os endpoints
WebSocket(ws://ewss://) usando os padrões"http://..."e"https://..."emConnection-Allowlist, porque o navegador normaliza os esquemas WebSocket para HTTP/HTTPS antes de fazer a correspondência. - É RECOMENDÁVEL incluir destinos de navegação externa esperados (
<a href>,<form action>, provedores de OAuth/SSO, finalizações de pagamento) na lista de permissões de um documento restrito. Lembre-se de que oredirects=blockpadrão bloqueia até mesmo redirecionamentos3xxde mesma origem, a menos que; redirects=allowseja especificado. - NÃO coloque
response-originou valores de parâmetro de lista interna (; report-to=conn-reports,; redirects=allow,; webrtc=allow) entre aspas nem separe itens dentro de(...)com vírgulas. - NÃO use grupos de expressões regulares personalizados, como
(\d+), ou caminhos relativos, como"/api/*", em padrões de URL. O analisador de serviço de rede do navegador rejeita grupos e padrões de regex sem um esquema e um nome de host. - NÃO use o atributo experimental
<iframe connectionAllowlist="...">ou o cabeçalho de aplicação incorporadoAllow-Connection-Allowlist-Fromna produção, porque a aplicação incorporada não está ativada por padrão nos navegadores de envio.
Estratégia de fallback
Como o Connection-Allowlist é aplicado na camada de rede do navegador por um cabeçalho de resposta HTTP, não é possível usar um polyfill no JavaScript do lado do cliente. Se o seu Baseline incluir navegadores que ainda não oferecem suporte a Connection-Allowlist, pareie Connection-Allowlist com um cabeçalho Content-Security-Policy (CSP) (e um elemento <meta http-equiv="Content-Security-Policy"> para busca no nível do documento e restrições de subrecursos quando os cabeçalhos de resposta HTTP não puderem ser configurados):
- Defesa em profundidade aditiva: os navegadores compatíveis aplicam as duas políticas simultaneamente. O
Connection-Allowlistbloqueia canais de exfiltração que não são da CSP (dns-prefetch,preconnect, WebRTC e redirecionamentos), enquanto a CSP restringe as navegações defetch(), subrecursos e<form action>em navegadores semConnection-Allowlist. - Cubra
form-actioneworker-srcexplicitamente: na CSP,default-srcnão volta para envios de<form action>. Sempre incluaform-actionexplicitamente. Incluaworker-src 'self'(excetoblob:) para que as páginas restritas não possam gerar SharedWorkersblob:para ignorar as restrições no nível do documento. - Mapear origens permitidas para diretivas de CSP por recurso: ao contrário de
Connection-Allowlist(que usa uma única lista de endpoints e não controla a marcação inline),default-src 'self'na CSP restringe cada tipo de recurso (connect-src,img-src,style-src,font-src,script-src) e bloqueia blocos inline<style>e<script>, a menos que sejam autorizados por hashes ou nonces. - Incluir
wss://na CSPconnect-src: embora oConnection-Allowlistcorresponda às conexõeswss://com os padrões"https://...", a CSPconnect-srcem ambientes de vários navegadores precisa listar explicitamente as origenswss://quando os WebSockets são usados.
Content-Security-Policy: default-src 'self'; connect-src 'self' https://api.example.com https://realtime.example.com wss://realtime.example.com; img-src 'self' https://*.cdn.example.com data: blob:; form-action 'self'; worker-src 'self'; object-src 'none'; base-uri 'self'; report-to conn-reports
<!-- Document-level fallback when HTTP response headers cannot be set on static hosting -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; connect-src 'self' https://api.example.com https://realtime.example.com wss://realtime.example.com; img-src 'self' https://*.cdn.example.com data: blob:; form-action 'self'; worker-src 'self'; object-src 'none'; base-uri 'self'"
/>