Skip to main content
All Guides

TLS / HTTPS

Weight: 5% of your AX score. Many AI agents refuse to fetch plaintext HTTP and downgrade trust on misconfigured TLS. This check verifies HTTPS, the HTTP→HTTPS redirect, and HSTS configuration including preload eligibility.

Invalid URL

Pass a fully qualified URL including scheme: npx ax-audit https://your-site.com. Bare hostnames like your-site.com are rejected.


Site not served over HTTPS

Your site responds to HTTP only. AI crawlers and most agents refuse plaintext origins.

  • Free option: Let's Encrypt + Certbot on your server, or your hosting provider's built-in TLS (Vercel, Netlify, Cloudflare, Render, Fly.io all give it for free).
  • Cloudflare Universal SSL: proxy your domain through Cloudflare for instant HTTPS.
  • Self-hosted: Caddy auto-provisions and renews certificates with zero config.

HTTP does not redirect to HTTPS

A request to http://your-site.com returned content over plain HTTP instead of redirecting. Agents may cache the insecure variant.

# Nginx
server {
  listen 80;
  server_name your-site.com www.your-site.com;
  return 301 https://your-site.com$request_uri;
}

# Apache (.htaccess)
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

# Caddy
http://your-site.com {
  redir https://your-site.com{uri} permanent
}

Redirect could not be verified

ax-audit got an unexpected response from the HTTP endpoint and couldn't determine the redirect behavior. Test manually:

curl -I http://your-site.com
# Expected: HTTP/1.1 301 Moved Permanently
#           Location: https://your-site.com/

No HSTS header

Strict-Transport-Security instructs browsers and agents to use HTTPS for all future requests for a fixed period. Without it, the very first connection on a new device can still be downgraded.

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
# Nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

# Express
app.use((req, res, next) => {
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
  next();
});

# Vercel (vercel.json)
{
  "headers": [{
    "source": "/(.*)",
    "headers": [{
      "key": "Strict-Transport-Security",
      "value": "max-age=31536000; includeSubDomains; preload"
    }]
  }]
}

HSTS missing max-age

Your HSTS header has no max-age directive — without it the header is effectively ignored. Set a numeric value in seconds:

Strict-Transport-Security: max-age=31536000

HSTS max-age too short

A max-age below ~6 months is too short to provide meaningful protection. The browser preload list requires at least 1 year (31536000 seconds).

Recommended values: max-age=63072000 (2 years) for production, max-age=300 only while testing.


HSTS missing includeSubDomains

includeSubDomains applies HSTS to api.your-site.com, docs.your-site.com, etc. — without it, an attacker can downgrade http://api.your-site.com even when the apex is locked to HTTPS.

Caveat
Verify every subdomain you own already serves HTTPS before enabling this flag. Once HSTS is cached by a client, an unreachable subdomain stays unreachable for the entire max-age period.

HSTS missing preload directive

Adding preload to the HSTS header signals you want to be added to the browser preload list — a hard-coded list of HTTPS-only domains shipped with Chromium, Firefox, Safari, and Edge.

After adding the directive and confirming the preload requirements, submit the domain at hstspreload.org.


HSTS preload not eligible

The preload directive is set but the header doesn't satisfy the preload list requirements. To be eligible:

  • max-age must be at least 31536000 (1 year).
  • includeSubDomains must be present.
  • preload must be present.
  • The apex domain must redirect from HTTP to HTTPS.
  • All subdomains must serve HTTPS.

Verify your status at hstspreload.org.