UnveilTech

UnveilScan Blog

← All articles

Try UnveilScan free

What 89 checkers actually return on a fresh nginx server

Posted 2026-04-29 · 8 min read · tutorialnginx

We span up a Debian 13 VM, ran apt install nginx, pointed an A record at it, generated a Let's Encrypt cert, enabled HTTPS with the default snippets nginx ships, and fired UnveilScan Extended at it. The score: 54 / D. This is what every vanilla nginx looks like to a serious scanner. Below is the full finding list, ranked by how to fix in order.

Critical (the deal-breakers)

CRITICAL tls.legacy_protocol_enabled

Default nginx ships with ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3 in /etc/nginx/nginx.conf on Debian 13 (yes, in 2026). Fix in one line:

ssl_protocols TLSv1.2 TLSv1.3;

High

HIGH web.server_header_verbose

nginx leaks its version: Server: nginx/1.26.0.

# In nginx.conf, http block:
server_tokens off;

HIGH tls.ssl_session_tickets_unrotated

nginx generates one ticket key at startup and never rotates it; that key would decrypt every prior session if leaked.

ssl_session_tickets off;
# OR rotate manually with ssl_session_ticket_key + cron

Medium

MEDIUM web.hsts_missing

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

MEDIUM web.csp_missing

Start with a permissive CSP (cf. our CSP article):

add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'; base-uri 'self'" always;

MEDIUM dns.no_caa

No CAA record. Add one to limit which CA can issue for your domain:

# BIND zone
example.com. IN CAA 0 issue "letsencrypt.org"
example.com. IN CAA 0 iodef "mailto:security@example.com"

MEDIUM email.spf_missing / email.dmarc_missing

Even if you don't send mail, set explicit reject records to prevent spoofing:

example.com. IN TXT "v=spf1 -all"
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"

Low

LOW web.xcto_missing / web.xfo_missing / web.referrer_policy_missing / web.permissions_policy_missing

Cookie-cutter headers, drop them in once and forget:

add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "no-referrer" always;
add_header Permissions-Policy "interest-cohort=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;

LOW web.security_txt_missing

$ mkdir -p /var/www/html/.well-known
$ cat > /var/www/html/.well-known/security.txt <<'EOF'
Contact: mailto:security@example.com
Expires: 2027-04-29T00:00:00Z
Preferred-Languages: en
EOF

LOW dns.no_aaaa

No IPv6 record. Add an AAAA pointing at your IPv6 address (most VPS now have one for free).

LOW tls.no_dane_tlsa

DANE TLSA record absent. Optional unless you run mail; skip if you don't.

The fix-everything snippet

One nginx config block that addresses 80% of the above. Drop in your server { }:

# TLS hardening
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE+AESGCM:ECDHE+CHACHA20:!aNULL:!MD5;
ssl_session_tickets off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "no-referrer" always;
add_header Permissions-Policy "interest-cohort=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'; base-uri 'self'" always;

# Hide leaks
server_tokens off;

# Block sensitive files
location ~ /\. { deny all; return 404; }
location ~ \.(bak|sql|env|swp|tar\.gz)$ { deny all; return 404; }

Plus the DNS records above (CAA, SPF, DMARC, AAAA), and security.txt.

After applying everything

We re-scanned the same VM. Score: 91 / A. The remaining 9 points come from environmental findings (single-ASN hosting, no DNSSEC) that need infrastructure changes. For application-layer security on a default-config nginx, this snippet gets you from D to A in 5 minutes.

Verify the fix on your nginx

Apply the snippet, run a Basic scan, watch the score climb live.

Scan a domain