Skip to content

Cloudflare Worker — OLP Gateway v2

Última atualização: 2026-02-23 Código-fonte: Gerenciado externamente no painel Cloudflare (não faz parte deste repositório) URL de produção: https://gateway.olp.digital


1. Por Que Existe

O Worker resolve dois problemas que o Supabase sozinho não consegue:

ProblemaCausaSolução do Worker
Cookies cross-originEdge Functions do Supabase estão em *.supabase.co, domínio diferente de olp.digital. Set-Cookie com SameSite=None exige Domain corretoReescreve Domain dos cookies olp_auth e olp_mural para .olp.digital
GeolocalizaçãoSupabase não expõe dados de geo do cliente. logging-helper.ts precisa de cidade/estado/país para auditoriaInjeta headers X-Geo-* extraídos de request.cf (dados nativos do Cloudflare)

Sem o Worker: Login funciona apenas em localhost/preview (cookies first-party). Em produção, cookies não são aceitos pelo browser.


2. Ativação e Roteamento (Frontend)

O uso do Worker é opcional e controlado por variáveis de ambiente no .env:

env
# Ativar gateway (produção)
VITE_USE_WORKER=true
VITE_WORKER_URL=https://gateway.olp.digital

# Desativar gateway (desenvolvimento/preview)
VITE_USE_WORKER=false
# ou simplesmente não definir as variáveis

Lógica no src/lib/edge-function.ts

if (VITE_USE_WORKER === 'true' && VITE_WORKER_URL) {
  baseUrl = VITE_WORKER_URL        // gateway.olp.digital
} else {
  baseUrl = VITE_SUPABASE_URL      // *.supabase.co (direto)
}

Quando Usar Cada Modo

ContextoVITE_USE_WORKERMotivo
localhost:5173falseCookies first-party funcionam naturalmente
Preview LovablefalseCookies first-party, sem necessidade de reescrita
Produção (olp.digital)trueNecessário para cookies cross-origin e geolocalização

3. Fluxo de Requisição

┌─────────────┐     ┌──────────────────────┐     ┌────────────────────┐
│   Frontend   │────▶│  gateway.olp.digital  │────▶│  *.supabase.co     │
│  olp.digital │     │  (Cloudflare Worker)  │     │  Edge Functions    │
└─────────────┘     └──────────────────────┘     └────────────────────┘
                            │                            │
                     ┌──────┴──────┐              ┌──────┴──────┐
                     │ Injeta:     │              │ Retorna:    │
                     │ X-Geo-City  │              │ Set-Cookie  │
                     │ X-Geo-Region│              │ JSON body   │
                     │ X-Geo-Country│             └─────────────┘
                     │ X-Geo-Timezone│
                     │ X-Real-IP   │
                     │ X-Forwarded-For│
                     │ X-Gateway   │
                     └─────────────┘

                     ┌──────┴──────┐
                     │ Reescreve:  │
                     │ Set-Cookie  │
                     │ Domain=     │
                     │ .olp.digital│
                     └─────────────┘

4. Funcionalidades Detalhadas

4.1 Validação CORS

O Worker implementa sua própria validação CORS (independente do cors-helpers.ts das Edge Functions):

Origens permitidas (lista estática):

http://localhost:5173
http://localhost:8080
https://olp.digital

Padrões dinâmicos (regex):

*.lovableproject.com
*.lovable.app
*.olp.digital

Regras:

  • Origin presente + não permitido → 403 Forbidden
  • Origin ausente (curl/server-to-server) → Passa sem headers ACAO
  • Origin válido → ACAO com origin exato + credentials: true
  • Preflight (OPTIONS) → 204 No Content com headers CORS
  • Max-Age: 86400 (cache de preflight por 24h)

Nota: O Worker e as Edge Functions possuem listas CORS separadas. Ambas devem estar sincronizadas. A do Worker está no código do Worker (Cloudflare). A das Edge Functions está em supabase/functions/_shared/cors-helpers.ts.

4.2 Reescrita de Cookies

Cookies monitorados: olp_auth, olp_mural

Lógica de reescrita por origem:

OrigemReescreve Domain?Motivo
localhost / 127.0.0.1Dev — cookies first-party
*.lovableproject.comPreview — cookies first-party
*.lovable.appPreview — cookies first-party
*.olp.digital✅ → .olp.digitalProdução — cross-origin necessário
Sem origin (curl)✅ → .olp.digitalAssume produção

Detalhes técnicos:

  • Usa response.headers.getSetCookie() (array) em vez de .get('set-cookie') (string única) para capturar todos os headers Set-Cookie
  • Cookies de remoção (Max-Age=0, usados no logout) não são reescritos — passam intactos
  • Cada cookie processado é adicionado via headers.append() (não set()) para preservar múltiplos

4.3 Injeção de Geolocalização

Headers injetados a partir de request.cf (dados nativos do Cloudflare):

HeaderFonteExemplo
X-Geo-Cityrequest.cf.citySão Paulo
X-Geo-Regionrequest.cf.regionSão Paulo
X-Geo-Countryrequest.cf.countryBR
X-Geo-Timezonerequest.cf.timezoneAmerica/Sao_Paulo
X-OLP-Client-IPCF-Connecting-IP200.xxx.xxx.xxx
X-Real-IPCF-Connecting-IP200.xxx.xxx.xxx
X-Forwarded-ForCF-Connecting-IP200.xxx.xxx.xxx
X-GatewayFixoolp-gateway-v2

Nota: X-OLP-Client-IP é o header preferido para captura de IP real. O Supabase não o sobrescreve, ao contrário de X-Real-IP. Ver IP_RETENTION_LGPD.md.

Consumo no backend: logging-helper.tsextractGeoFromHeaders(req) lê os headers X-Geo-*, e extractIP(req) prioriza X-OLP-Client-IP.

4.4 Proxy de Headers

Headers preservados do request original (frontend → Supabase):

authorization, apikey, content-type, cookie, accept,
x-client-info, x-supabase-api-version

Se apikey não estiver presente, o Worker injeta SUPABASE_ANON_KEY automaticamente.

4.5 Timeout e Erros

CenárioStatusMensagem
Timeout (>25s)504Gateway timeout - tente novamente
Erro de rede502Erro de conexão - tente novamente
SUPABASE_URL não configurado500Gateway misconfigured

Decisão de design (MVP): Sem fallback 503, sem retry automático. O frontend exibe o erro e o usuário pode tentar novamente.

4.6 Burst Rate Limiting (por IP)

Implementado em: 2026-03-08

O Worker rastreia requisições por IP usando um Map in-memory com sliding window de 5 segundos. Se um IP exceder 150 requests em 5s, o Worker injeta o header X-Burst-Blocked: true na requisição encaminhada ao Supabase.

Por que no Worker e não na Edge Function:

  • Cloudflare Workers operam como processo único por edge location (sem fragmentação de isolates)
  • Edge Functions do Supabase escalam em múltiplos isolates sob carga, cada um com seu próprio Map vazio
  • O Worker garante contagem cross-request precisa

Código a adicionar no Worker (Cloudflare Dashboard):

javascript
// ============= BURST RATE LIMITER =============
const BURST_LIMIT = { max: 150, windowMs: 5000 };
const burstTracker = new Map(); // IP -> timestamp[]

function checkBurst(ip) {
  const now = Date.now();
  const timestamps = (burstTracker.get(ip) || []).filter(t => now - t < BURST_LIMIT.windowMs);
  timestamps.push(now);
  burstTracker.set(ip, timestamps);
  
  // Cleanup periódico
  if (burstTracker.size > 10000) {
    for (const [key, ts] of burstTracker) {
      if (now - ts[ts.length - 1] > BURST_LIMIT.windowMs * 2) burstTracker.delete(key);
    }
  }
  
  return timestamps.length <= BURST_LIMIT.max;
}

// No handler fetch(), ANTES de fazer o fetch ao Supabase:
// Apenas para rotas do portal-escola
const url = new URL(request.url);
if (url.pathname.includes('/portal-escola')) {
  const clientIP = request.headers.get('CF-Connecting-IP') || 'unknown';
  if (!checkBurst(clientIP)) {
    headers.set('X-Burst-Blocked', 'true');
  }
}

Consumo na Edge Function (portal-escola/index.ts):

  • Se X-Burst-Blocked: true → retorna 429 imediatamente
  • Se X-Gateway: olp-gateway-v2 (veio pelo Worker sem bloqueio) → pula burst check
  • Sem header de gateway (dev/preview) → usa fallback in-memory local

5. Variáveis de Ambiente do Worker (Cloudflare)

Configuradas no painel do Cloudflare Workers:

VariávelDescrição
SUPABASE_URLURL do projeto Supabase (https://mjvuzsizjlcalyfmbquy.supabase.co)
SUPABASE_ANON_KEYChave anon (fallback se frontend não enviar apikey)

6. Sincronização Obrigatória

O Worker é um componente externo ao repositório. Alterações devem ser sincronizadas manualmente:

AlteraçãoOnde atualizar
Nova origem permitidaWorker (ALLOWED_ORIGINS) E cors-helpers.ts
Novo cookie JWTWorker (COOKIES_TO_REWRITE) E Edge Functions que emitem o cookie
Novo header preservadoWorker (PRESERVE_HEADERS)
Novo header de webhookWorker (PRESERVE_HEADERS) E Edge Function que valida o header
Mudança de domínioWorker (PRODUCTION_DOMAIN) E DNS

⚠️ Webhooks externos (Wasender, Mercado Pago, etc.): Headers de autenticação customizados (ex: x-webhook-secret, x-webhook-signature) devem ser adicionados explicitamente ao PRESERVE_HEADERS do Worker. Caso contrário, o gateway remove esses headers antes de encaminhar ao Supabase, causando 401 na Edge Function. Alternativa: configurar webhooks para apontar diretamente à URL do Supabase (*.supabase.co), ignorando o gateway.


7. Como Editar o Worker

  1. Acessar Cloudflare Dashboard → Workers & Pages
  2. Selecionar o Worker olp-gateway (ou nome configurado)
  3. Editar no editor online ou fazer deploy via Wrangler CLI
  4. Testar com curl:
bash
# Teste básico (sem origin)
curl -v https://gateway.olp.digital/functions/v1/auth-diagnostics \
  -H "Content-Type: application/json" \
  -d '{"action":"public_smoke"}'

# Teste com origin de produção
curl -v https://gateway.olp.digital/functions/v1/me \
  -H "Origin: https://olp.digital" \
  -H "Content-Type: application/json" \
  -H "Cookie: olp_auth=eyJ..."

8. Troubleshooting

SintomaCausa provávelSolução
Login funciona no preview mas não em produçãoWorker não reescrevendo cookiesVerificar COOKIES_TO_REWRITE e shouldRewriteCookieDomain()
Geolocalização sempre "Portland, Oregon"req não passado para registrarLog()Adicionar req: req na chamada (ver AUDIT_LOG.md)
403 em preflightOrigin não está na lista do WorkerAdicionar em ALLOWED_ORIGINS ou ORIGIN_PATTERNS
504 Gateway timeoutEdge Function lenta (>25s)Otimizar a Edge Function ou aumentar FETCH_TIMEOUT
Cookies não removidos no logoutWorker reescrevendo cookie de remoçãoBug — verificar detecção de Max-Age=0
Logs sem geolocalizaçãoWorker não injetando geo headersVerificar request.cf e headers X-Geo-*
Webhook retorna 401Header de autenticação do webhook removido pelo WorkerAdicionar header (ex: x-webhook-signature) ao PRESERVE_HEADERS

9. Headers de Segurança HTTP

Implementado em: 2026-03-01 Status: ✅ Ativo em produção (verificado via curl -I)

O Worker injeta headers de segurança em todas as respostas retornadas ao browser, via um objeto SECURITY_HEADERS aplicado antes do return.

9.1 Headers Ativos (Produção)

javascript
const SECURITY_HEADERS = {
  'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
  'X-Frame-Options': 'DENY',
  'X-Content-Type-Options': 'nosniff',
  'Referrer-Policy': 'strict-origin-when-cross-origin',
  'Permissions-Policy': 'camera=(), microphone=(), geolocation=(), payment=()',
  'Content-Security-Policy': "default-src 'none'; frame-ancestors 'none'",
};
HeaderFinalidade
Strict-Transport-SecurityForça HTTPS por 1 ano. Só funciona via HTTP header (meta tag é ignorada pela spec RFC 6797).
X-Frame-OptionsPrevine Clickjacking (defesa em profundidade junto com CSP frame-ancestors).
X-Content-Type-OptionsPrevine MIME sniffing em respostas JSON da API.
Referrer-PolicyControla vazamento de URL no header Referer.
Permissions-PolicyRestringe APIs do browser (câmera, microfone, geolocalização, pagamento).
Content-Security-PolicyCSP restritiva para API: default-src 'none' (nenhum recurso carregado) + frame-ancestors 'none' (não embute em iframe). Adequada porque o Worker só retorna JSON.

9.2 Onde Estão no Código do Worker

Os headers são injetados após obter a resposta do Supabase e antes de retornar ao cliente:

javascript
// Aplicar headers de segurança em TODAS as respostas
for (const [key, value] of Object.entries(SECURITY_HEADERS)) {
  newResponse.headers.set(key, value);
}

9.3 CSP do Worker vs CSP do Frontend

ContextoCSPMotivo
Worker (API)default-src 'none'; frame-ancestors 'none'Respostas são JSON — nenhum recurso deve ser carregado
Frontend (index.html)default-src 'self'; script-src 'self' 'unsafe-inline'; ...Precisa carregar scripts, estilos, imagens, conectar a backends

Não há conflito: O CSP do Worker aplica-se às respostas da API (gateway.olp.digital/functions/v1/...). O CSP do frontend aplica-se ao documento HTML servido pela Lovable.

9.4 Headers no index.html vs Worker

Headerindex.html (meta tag)Worker (HTTP header)Notas
Content-Security-Policy✅ (permissiva para app)✅ (restritiva para API)Políticas diferentes, ambas corretas
X-Frame-OptionsDefesa em profundidade
X-Content-Type-OptionsCobre respostas de API também
Strict-Transport-Security❌ IgnoradoAtivoSó funciona via HTTP header
Referrer-PolicyDefesa em profundidade
Permissions-PolicyDefesa em profundidade

9.5 Sobre HSTS Preload

O flag ; preload pode ser adicionado ao HSTS para submissão na HSTS Preload List:

AçãoReversível?Efeito
Adicionar ; preload no header do Worker✅ Sim (remove e faz deploy)Apenas sinal de prontidão, sem efeito prático sozinho
Submeter em hstspreload.org❌ Praticamente não (3-6 meses para remover)Browsers forçam HTTPS antes da primeira requisição

Status atual: Sem ; preload. HSTS funciona normalmente — após a primeira visita HTTPS, o browser lembra por 1 ano. A janela mínima (primeira requisição HTTP) é mitigada pelo redirect 301 do Cloudflare.

Para adicionar preload no futuro:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Depois submeter em hstspreload.org. Requer que todos os subdomínios suportem HTTPS.


10. Referências

  • DEPLOYMENT.md — Visão geral de deploy e ambientes
  • AUTHENTICATION.md — Fluxo JWT e cookies
  • AUDIT_LOG.mdregistrarLog() com req: req
  • EDGE_FUNCTIONS_CATALOG.md — Catálogo de funções consumidas via gateway
  • SECURITY_AUDIT_2026-02-28.md — Auditoria de segurança (seção 9)
  • src/lib/edge-function.ts — Helper frontend que roteia via Worker
  • supabase/functions/_shared/cors-helpers.ts — CORS das Edge Functions (sincronizar com Worker)
  • supabase/functions/_shared/logging-helper.ts — Consumidor dos headers X-Geo-*