Set Up Nginx SSL Security Headers: Complete Production Guide

By Raman Kumar

Share:

Updated on May 11, 2026

Set Up Nginx SSL Security Headers: Complete Production Guide

Why Nginx SSL Security Headers Matter for Production Sites

Your SSL certificate encrypts traffic between your server and visitors. But without proper security headers, browsers won't enforce the strongest security policies.

Nginx SSL security headers tell browsers exactly how to handle your site's connections. They prevent downgrade attacks and ensure encrypted-only communication.

Production sites face constant scanning for security weaknesses. Headers like HSTS force HTTPS connections. X-Frame-Options prevents clickjacking attacks. Without these configured, your site remains vulnerable even with a valid SSL certificate.

This tutorial walks through setting up comprehensive security headers on Ubuntu VPS. You'll configure HSTS, CSP, and essential headers that protect your site and improve security scanner ratings.

Prerequisites and Initial Server Setup

You need an Ubuntu VPS with Nginx already installed and running. Your domain should have a valid SSL certificate configured - either through Let's Encrypt or a commercial certificate.

Check your current Nginx configuration:

sudo nginx -t
sudo systemctl status nginx

Both commands should show clean results. Your site should already serve HTTPS traffic before adding security headers.

Back up your current Nginx configuration:

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.backup
sudo cp -r /etc/nginx/conf.d /etc/nginx/conf.d.backup

For sites running on Hostperl VPS hosting, these backup commands let you quickly restore if configuration changes cause issues.

Configure HSTS (HTTP Strict Transport Security)

HSTS forces browsers to connect over HTTPS only. Once a browser sees the HSTS header, it won't make HTTP requests to your domain for the specified time period.

Create a security headers configuration file:

sudo nano /etc/nginx/conf.d/security-headers.conf

Add the HSTS configuration:

# HSTS Configuration
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

The max-age=31536000 sets HSTS for one year. The includeSubDomains directive applies HSTS to all subdomains. The preload flag allows submission to browser HSTS preload lists.

For production sites with subdomains, be careful with includeSubDomains. If any subdomain can't support HTTPS, browsers will block access entirely.

Add Content Security Policy Headers

Content Security Policy (CSP) prevents XSS attacks by controlling which resources browsers can load. Add this to your security headers file:

# Content Security Policy
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https:; connect-src 'self'; frame-ancestors 'none'" always;

This restrictive CSP allows:

  • 'self' - Resources from your own domain
  • 'unsafe-inline' - Inline scripts and styles (needed for many CMS platforms)
  • data: https: for images - Data URLs and HTTPS image sources
  • frame-ancestors 'none' - Prevents your site from being embedded in frames

Start with a permissive CSP, then tighten it based on your site's actual resource needs. Check browser developer tools for CSP violations during testing.

Configure Essential Security Headers

Add these critical security headers to your configuration:

# Prevent MIME type sniffing
add_header X-Content-Type-Options "nosniff" always;

# Enable XSS protection
add_header X-XSS-Protection "1; mode=block" always;

# Control iframe embedding
add_header X-Frame-Options "SAMEORIGIN" always;

# Control referrer information
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# Feature policy for modern browsers
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;

Each header serves a specific security purpose:

  • X-Content-Type-Options: Prevents browsers from MIME-sniffing responses
  • X-XSS-Protection: Enables browser XSS filtering
  • X-Frame-Options: Controls whether your site can be embedded in frames
  • Referrer-Policy: Controls referrer information sent to other sites
  • Permissions-Policy: Disables unnecessary browser APIs

Apply Headers to Virtual Host Configuration

Now include your security headers in your site's virtual host configuration. Edit your site configuration:

sudo nano /etc/nginx/sites-available/your-domain.com

Add the include directive inside your server block:

server {
    listen 443 ssl http2;
    server_name your-domain.com www.your-domain.com;
    
    ssl_certificate /path/to/your/certificate.crt;
    ssl_certificate_key /path/to/your/private.key;
    
    # Include security headers
    include /etc/nginx/conf.d/security-headers.conf;
    
    # Your existing configuration...
    root /var/www/html;
    index index.html index.php;
    
    location / {
        try_files $uri $uri/ =404;
    }
}

The include directive pulls in all the security headers you configured. This keeps your main site configuration clean while applying consistent security policies.

For sites with multiple domains, this approach lets you maintain security headers in one file while applying them across all virtual hosts.

Test and Validate Header Configuration

Test your Nginx configuration for syntax errors:

sudo nginx -t

If the test passes, reload Nginx:

sudo systemctl reload nginx

Verify your headers work using curl:

curl -I https://your-domain.com

You should see your security headers in the response. Look for lines like:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN

Use online tools like Security Headers or Mozilla Observatory to get a comprehensive security analysis.

Advanced Header Configurations for Different Use Cases

WordPress sites need modified CSP to allow admin functionality:

# WordPress-specific CSP
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://your-domain.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https: *.gravatar.com" always;

E-commerce sites using payment processors need to allow external connections:

# E-commerce CSP example
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://js.stripe.com https://checkout.paypal.com; frame-src https://js.stripe.com https://www.paypal.com; connect-src 'self' https://api.stripe.com" always;

For API endpoints, you might want to remove X-Frame-Options to allow legitimate iframe embedding:

location /api/ {
    # Remove X-Frame-Options for API endpoints
    add_header X-Frame-Options "" always;
    # Keep other security headers
    include /etc/nginx/conf.d/security-headers.conf;
}

Monitor and Maintain Security Headers

Set up monitoring to catch header configuration issues. Create a simple script to check headers:

#!/bin/bash
# check-headers.sh

URL="https://your-domain.com"
RESPONSE=$(curl -I -s "$URL")

if echo "$RESPONSE" | grep -q "Strict-Transport-Security"; then
    echo "HSTS: OK"
else
    echo "HSTS: Missing"
    exit 1
fi

if echo "$RESPONSE" | grep -q "X-Content-Type-Options"; then
    echo "X-Content-Type-Options: OK"
else
    echo "X-Content-Type-Options: Missing"
    exit 1
fi

echo "All security headers present"

Make it executable and add to your monitoring:

chmod +x check-headers.sh
./check-headers.sh

Consider integrating this check with your existing monitoring system. Run it via cron to catch configuration drift.

For detailed information on setting up additional email security, see our guide on DKIM email authentication setup.

Troubleshoot Common Header Issues

Headers not appearing usually means the include directive isn't in the right server block. Make sure it's inside the server {} block handling HTTPS traffic, not HTTP.

Duplicate headers occur when headers are defined in multiple places. Remove headers from individual location blocks if you're including them globally.

Site functionality breaking after adding CSP indicates resources being blocked. Check browser developer tools Network tab for blocked requests. Adjust your CSP policy to allow legitimate resources.

HSTS preload rejection happens when your site doesn't meet preload list requirements. Make sure HTTPS works on your root domain and all subdomains before submitting to preload lists.

For VPS hosting environments, header configuration problems often stem from incorrect file paths or permissions. Verify Nginx can read your configuration files:

ls -la /etc/nginx/conf.d/security-headers.conf
sudo nginx -T | grep "add_header"

Ready to secure your production site with proper SSL and security headers? Hostperl VPS hosting provides the performance and control you need to implement advanced Nginx configurations.

Frequently Asked Questions

Should I enable HSTS preloading for my production site?

Enable HSTS preloading only if you're certain all subdomains can support HTTPS permanently. Once preloaded, browsers will refuse HTTP connections even if you remove the HSTS header. Test thoroughly with includeSubDomains before adding preload.

How strict should my Content Security Policy be?

Start with a permissive CSP that doesn't break functionality, then gradually tighten it. Use CSP reporting to identify which resources your site actually uses. A working site with moderate CSP beats a broken site with strict CSP.

Do security headers impact page loading performance?

Security headers add minimal overhead - typically less than 1KB to response headers. The security benefits far outweigh any negligible performance impact. Modern browsers handle security headers efficiently.

Can I apply different security headers to different parts of my site?

Yes, use location blocks to apply different headers to specific paths. This works well for APIs that need different CSP policies or admin areas that require less restrictive frame options. Always make sure at least basic security headers apply site-wide.

What happens if I misconfigure security headers on a live site?

Misconfigured headers can break site functionality, especially CSP and HSTS. Always test header changes on a staging environment first. Keep configuration backups and know how to quickly revert changes. Most header misconfigurations are recoverable with a simple Nginx reload.