Cache de arquivos estáticos com Nginx

    Servir arquivos estáticos diretamente pelo Nginx — sem passar pelo Node.js ou Python — é 10-100x mais rápido. Com cache de proxy configurado, o Nginx armazena respostas do backend e as serve para requisições subsequentes sem chegar na aplicação. Isso reduz latência, carga no backend e consumo de memória.

    Servir arquivos estáticos diretamente

    Para arquivos que ficam no disco da VPS, sirva direto pelo Nginx:

    nginx
    server {
        listen 443 ssl http2;
        server_name seudominio.com.br;
    
        # Diretório raiz dos estáticos
        root /var/www/html;
    
        # Servir arquivos estáticos sem passar pelo backend
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|webp|woff|woff2|ttf|eot)$ {
            expires 1y;                        # cache por 1 ano no browser
            add_header Cache-Control "public, immutable";
            add_header Vary "Accept-Encoding";
            access_log off;                    # não logar para economizar IO
            try_files $uri =404;
        }
    
        # HTML: sem cache (sempre servir versão atualizada)
        location ~* \.html$ {
            expires -1;
            add_header Cache-Control "no-store, no-cache, must-revalidate";
        }
    
        # API: sem cache
        location /api/ {
            proxy_pass http://127.0.0.1:3000;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    
        # Tentar arquivo estático, fallback para a app
        location / {
            try_files $uri $uri/ @app;
        }
    
        location @app {
            proxy_pass http://127.0.0.1:3000;
        }
    }
    Dica
    Cache-Control: immutable diz ao browser que o arquivo nunca muda (para a URL dada) — sem revalidação mesmo após expirar. Use com assets com hash no nome (bundle.abc123.js). Para arquivos sem hash: use Cache-Control: public, max-age=86400 em vez de immutable.

    Proxy cache: armazenar respostas do backend

    O Nginx pode cachear respostas HTTP do backend em disco:

    nginx
    # /etc/nginx/nginx.conf — dentro do bloco http {}
    proxy_cache_path /var/cache/nginx
        levels=1:2
        keys_zone=api_cache:10m
        max_size=1g
        inactive=60m
        use_temp_path=off;
    
    # No bloco server:
    server {
        location /api/produtos {
            # Cachear respostas GET por 5 minutos
            proxy_cache api_cache;
            proxy_cache_valid 200 5m;
            proxy_cache_valid 404 1m;
            proxy_cache_methods GET HEAD;
            proxy_cache_key "$scheme$request_method$host$request_uri";
    
            # Usar cache mesmo quando backend estiver offline
            proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
            proxy_cache_background_update on;
            proxy_cache_lock on;       # apenas 1 req passa ao backend por key (thundering herd)
    
            # Adicionar header mostrando se veio do cache
            add_header X-Cache-Status $upstream_cache_status;
    
            proxy_pass http://127.0.0.1:3000;
        }
    }

    Compressão gzip e brotli

    Comprimir respostas reduz o tamanho em 60-80% para texto/JSON:

    nginx
    # /etc/nginx/conf.d/compression.conf
    
    # Gzip (suporte universal)
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;       # nível 1-9: 6 é bom balanço velocidade/compressão
    gzip_min_length 1024;    # não comprimir arquivos < 1 KB
    gzip_types
        text/plain
        text/css
        text/javascript
        application/javascript
        application/json
        application/xml
        image/svg+xml
        font/woff
        font/woff2;
    
    # Brotli (melhor compressão que gzip, suporte browsers modernos)
    # Requer módulo ngx_brotli (não incluído por padrão):
    # sudo apt install libbrotli-dev
    # Compilar Nginx com --add-module=ngx_brotli
    # Ou usar nginx-extra: sudo apt install nginx-extras
    brotli on;
    brotli_comp_level 6;
    brotli_types
        text/plain
        text/css
        application/javascript
        application/json
        image/svg+xml;
    
    # Servir arquivos pré-comprimidos (.gz ou .br) se existirem:
    gzip_static on;
    # brotli_static on;

    Invalidar o proxy cache

    Limpar o cache quando o conteúdo do backend é atualizado:

    nginx
    # Limpar cache por chave específica:
    sudo find /var/cache/nginx -type f -name "*.cache" | head -10
    
    # Limpar todo o cache de uma zona:
    sudo rm -rf /var/cache/nginx/*
    sudo nginx -s reload
    
    # Purge seletivo com ngx_cache_purge (módulo third-party):
    # Adicionar ao location:
    location ~ /purge(/.*) {
        allow 127.0.0.1;
        deny all;
        proxy_cache_purge api_cache $scheme$request_method$host$1;
    }
    
    # Chamar a partir do deploy script:
    curl -X PURGE http://127.0.0.1/purge/api/produtos
    
    # Forçar bypass do cache (sem purge) — útil para testes:
    location /api/ {
        # Bypass se o header X-No-Cache estiver presente:
        proxy_cache_bypass $http_x_no_cache;
        proxy_no_cache $http_x_no_cache;
        proxy_cache api_cache;
        proxy_pass http://127.0.0.1:3000;
    }
    # Testar: curl -H "X-No-Cache: 1" https://api.seudominio.com.br/api/produtos

    Verificar eficiência do cache

    Monitorar o hit rate e identificar o que está sendo cacheado:

    bash
    # Verificar o header X-Cache-Status na resposta:
    curl -I https://api.seudominio.com.br/api/produtos
    # X-Cache-Status: MISS    → primeira requisição, foi ao backend
    # X-Cache-Status: HIT     → servido do cache
    # X-Cache-Status: EXPIRED → cache expirado, foi ao backend
    # X-Cache-Status: STALE   → cache expirado mas backend offline
    # X-Cache-Status: BYPASS  → forçado a ir ao backend
    
    # Calcular hit rate nos logs:
    sudo awk '{print $NF}' /var/log/nginx/access.log \
      | grep -oP 'X-Cache-Status: \K\w+' \
      | sort | uniq -c
    
    # Alternativa — logar o cache status:
    log_format cache_log '$remote_addr - [$time_local] "$request" $status '
                         '$upstream_cache_status "$http_referer"';
    access_log /var/log/nginx/cache.log cache_log;
    
    # Tamanho atual do cache em disco:
    sudo du -sh /var/cache/nginx/

    $ runstack deploy --plan starter

    Não quer configurar manualmente?

    Não quer configurar manualmente? Implante o VPS em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.

    Perguntas frequentes