kb.erickguedes.com
Nginx: Performance e Proxy Reverso

Gzip, Headers de Segurança e CORS

Aula 5 de 6

Compressão com Gzip

Configuração Básica

# No bloco http (nginx.conf)
gzip on;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_proxied any;
gzip_vary on;
gzip_disable "msie6";

gzip_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/javascript
    application/json
    application/xml
    application/rss+xml
    application/atom+xml
    image/svg+xml
    font/ttf
    font/opentype
    application/vnd.ms-fontobject;

Gzip Static (arquivos pré-compactados)

gzip -k style.css
gzip -k -9 script.js

find /var/www/site/public -type f \( -name "*.css" -o -name "*.js" -o -name "*.html" \) -exec gzip -k {} \;
gzip_static on;

Gunzip (descompressão)

gunzip on;

location / {
    gunzip on;
    proxy_pass http://backend;
}

Minificação + Gzip

sudo npm install -g uglify-js clean-css-cli

uglifyjs script.js -o script.min.js -c -m
cleancss -o style.min.css style.css

gzip -k -9 script.min.js style.min.css

Headers de Segurança

Configuração Completa

server {
    listen 443 ssl http2;
    server_name site.exemplo.com;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

    add_header Content-Security-Policy "
        default-src 'self';
        script-src 'self' https://analytics.exemplo.com;
        style-src 'self' 'unsafe-inline';
        img-src 'self' data: https://cdn.exemplo.com;
        font-src 'self' https://fonts.gstatic.com;
        connect-src 'self' https://api.exemplo.com;
        frame-ancestors 'none';
        base-uri 'self';
        form-action 'self';
    " always;
}

Headers por Tipo de Conteúdo

location ~ \.html$ {
    add_header Cache-Control "no-cache, no-store, must-revalidate";
    add_header Pragma "no-cache";
}

location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?|ttf)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    add_header X-Content-Type-Options "nosniff";
    access_log off;
}

location ~* \.(pdf|doc|docx|xls|xlsx|zip|tar|gz)$ {
    add_header Content-Disposition "attachment";
    expires 1d;
}

CORS (Cross-Origin Resource Sharing)

CORS Básico para API

server {
    listen 80;
    server_name api.exemplo.com;

    location / {
        add_header Access-Control-Allow-Origin "*" always;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With" always;
        add_header Access-Control-Expose-Headers "X-RateLimit-Remaining, X-RateLimit-Reset" always;
        add_header Access-Control-Max-Age 86400 always;

        if ($request_method = OPTIONS) {
            add_header Content-Length 0;
            add_header Content-Type text/plain;
            return 204;
        }

        proxy_pass http://backend;
    }
}

CORS Restrito para Domínios Específicos

map $http_origin $cors_origin {
    default "";
    ~^https?://(www\.)?meusite\.com$ $http_origin;
    ~^https?://app\.meusite\.com$ $http_origin;
    ~^https?://admin\.meusite\.com$ $http_origin;
}

map $request_method $cors_methods {
    OPTIONS "GET, POST, PUT, DELETE, OPTIONS, PATCH";
    default "";
}

server {
    location /api/ {
        if ($cors_origin) {
            add_header Access-Control-Allow-Origin "$cors_origin" always;
            add_header Access-Control-Allow-Methods "$cors_methods" always;
            add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
            add_header Access-Control-Allow-Credentials "true" always;
            add_header Access-Control-Max-Age 86400;
        }

        if ($request_method = OPTIONS) {
            add_header Content-Length 0;
            add_header Content-Type text/plain;
            return 204;
        }

        proxy_pass http://backend;
    }
}

Autenticação Básica (auth_basic)

sudo apt install apache2-utils -y
sudo htpasswd -c /etc/nginx/.htpasswd admin
sudo htpasswd /etc/nginx/.htpasswd usuario2
location /admin {
    auth_basic "Area Restrita";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

location /admin {
    satisfy any;
    allow 192.168.1.0/24;
    deny all;
    auth_basic "Area Restrita";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

Autenticação com auth_request

upstream auth_service {
    server auth.exemplo.com:8080;
}

server {
    location /protected {
        auth_request /auth;
        auth_request_set $auth_status $upstream_status;
        error_page 401 = @error401;
        proxy_pass http://backend;
    }

    location = /auth {
        internal;
        proxy_pass http://auth_service/validate;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-URI $request_uri;
        proxy_set_header X-Original-Method $request_method;
    }

    location @error401 {
        return 302 https://auth.exemplo.com/login?redirect=$request_uri;
    }
}

Módulo real_ip

set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
real_ip_header CF-Connecting-IP;

Lab: Site Seguro com Gzip e Headers

# 1. Criar estrutura do site
sudo mkdir -p /var/www/secure-site/{public,api}

echo "<h1>Headers de Seguranca Ativos</h1>" | sudo tee /var/www/secure-site/public/index.html
echo "body { font-family: sans-serif; }" | sudo tee /var/www/secure-site/public/style.css
echo "console.log('Seguro');" | sudo tee /var/www/secure-site/public/script.js

# 2. Pre-comprimir assets
gzip -k /var/www/secure-site/public/style.css
gzip -k /var/www/secure-site/public/script.js

# 3. Criar configuracao
cat << 'EOF' | sudo tee /etc/nginx/sites-available/secure-headers
gzip on;
gzip_comp_level 6;
gzip_min_length 256;
gzip_vary on;
gzip_proxied any;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
gzip_static on;

server {
    listen 80;
    server_name secure-headers.local;
    root /var/www/secure-site/public;
    index index.html;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    add_header Content-Security-Policy "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'; img-src 'self';" always;

    location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    location ~ \.html$ {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
    }

    location /api/ {
        add_header Access-Control-Allow-Origin "*" always;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
        add_header Access-Control-Max-Age 86400 always;

        if ($request_method = OPTIONS) {
            add_header Content-Length 0;
            add_header Content-Type text/plain;
            return 204;
        }

        proxy_pass http://127.0.0.1:3000;
    }

    location /admin {
        auth_basic "Admin Access";
        auth_basic_user_file /etc/nginx/.htpasswd;
    }

    access_log /var/log/nginx/secure-headers-access.log;
    error_log /var/log/nginx/secure-headers-error.log;
}
EOF

# 4. Criar senha admin
sudo htpasswd -c /etc/nginx/.htpasswd admin

# 5. Habilitar site
sudo ln -sf /etc/nginx/sites-available/secure-headers /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

# 6. Testar headers
curl -I http://secure-headers.local/ | grep -E "X-|Content-Security|Referrer"
curl -H "Accept-Encoding: gzip" -I http://secure-headers.local/style.css | grep -E "Content-Encoding|Vary"
curl -I http://secure-headers.local/api/ | grep -i access-control

Seguranca no Nginx e construida em camadas: compressao (gzip), headers protetivos (CSP, HSTS, X-Frame-Options), CORS controlado e autenticacao. Cada header adiciona uma barreira contra classes especificas de ataque.