Content-Security-Policy (CSP)
Stops inline JavaScript, script injection, and data exfiltration:
Content-Security-Policy: default-src 'self'; script-src 'self'; img-src 'self' data:; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com
This policy says: all resources come from the same origin by default. Scripts only from same-origin. Images from same-origin or data URIs. Styles from same-origin or Google Fonts. Any attempt to inline <script> or load scripts from attacker.com is blocked.
For sites using Vite's content hashing, you can use 'self' and 'unsafe-inline' is not needed — hashed assets are served from the same domain.
X-Frame-Options
Stops your site from being embedded in an iframe on another origin:
X-Frame-Options: SAMEORIGIN
An attacker can't do clickjacking: create a transparent iframe of your site, overlay a fake button, and trick users into clicking it. SAMEORIGIN means your pages can be framed by the same origin, but not by evil.com. Use DENY if you don't need iframe embedding at all.
X-Content-Type-Options
Stops the browser from guessing file types:
X-Content-Type-Options: nosniff
Without this, a file served as text/plain but containing JavaScript can be executed by the browser if it decides the content "looks like" JS. nosniff enforces that the MIME type sent by the server is what the browser treats the file as — no guessing.
Strict-Transport-Security (HSTS)
Forces all future connections to use HTTPS:
Strict-Transport-Security: max-age=31536000; includeSubDomains
The browser remembers this for one year. Even if an attacker tricks you into clicking an http:// link, the browser silently upgrades it to https://. Prevents downgrade attacks where an attacker intercepts traffic on an unencrypted connection.
nginx implementation
Add these headers in nginx.conf inside your server block:
server {
listen 80;
server_name evomedia.net www.evomedia.net;
# Security headers
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; img-src 'self' data:; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://evomedia:3000;
}
}
The always flag ensures headers are sent even on error responses. Since you're using nginx as a reverse proxy, these headers will be applied to all responses from your backend, whether it's a static file, an API response, or an error page.
After updating nginx.conf, hot-reload with nginx -s reload for zero downtime.