In my previous blog, we talked about How to configure Nginx as a load balancer, today we gonna talk about How to configure Security Headers in Nginx
- HTTP Strict Transport Security (HSTS)
- SSL (Secure Sockets Layer)
- Content Security Policy (CSP)
- X-XSS-Protection
- X-Frame-Options
- X-Content-Type-Options
- Access-Control-Allow-Origin
make sure to restart Nginx after each modification using this command
nginx -s reload
HTTP Strict Transport Security (HSTS)
When a user enters a web domain manually (providing the domain name without the http:// or https:// prefix) or follows a plain http:// link, the first request to the website is sent unencrypted, using plain HTTP. Most secured websites immediately send back a redirect to upgrade the user to an HTTPS connection, but a well‑placed attacker can mount a man‑in‑the‑middle (MITM) attack to intercept the initial HTTP request and can control the user’s session from then on.
HSTS seeks to deal with the potential vulnerability by instructing the browser that a domain can only be accessed using HTTPS. Even if the user enters or follows a plain HTTP link, the browser strictly upgrades the connection to HTTPS
How to configure Security Header HSTS in NGINX
Add this line below to the server block, Once you’re done save your changes and reload Nginx.
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"
The Nginx config file should lock like this.
upstream portal {
server localhost:8080;
}
server {
listen 80;
server_name portal.test;
# Security header for enforcing HTTPS
add_header Strict-Transport-Security "max-age=31536000";
location / {
proxy_pass http://portal;
}
}
SSL (Secure Sockets Layer)
SSL (Secure Sockets Layer) is a standard security technology for establishing an encrypted link between a server and a client—typically a web server and a browser. This ensures that all data passed between them remains private and integral. SSL is critical for protecting sensitive information, such as login credentials, credit card numbers, and personal data from eavesdropping or tampering. Modern implementations, like TLS (Transport Layer Security), have largely replaced SSL, but the term “SSL” is still commonly used to refer to this type of encryption. Enabling SSL on a website improves security and builds trust with users by ensuring safe data transmission over HTTPS.
How to configure Security Header SSL in NGINX
This block will enforce secure communication over HTTPS for your Nginx server.
upstream portal {
server localhost:8080;
}
server {
listen 80;
server_name portal.test;
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name portal.test;
# SSL certificate and key paths
ssl_certificate /etc/nginx/ssl/portal.crt;
ssl_certificate_key /etc/nginx/ssl/portal.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# HSTS for enforcing HTTPS
add_header Strict-Transport-Security "max-age=31536000" always;
# Security headers
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY";
location / {
proxy_pass http://portal;
}
}
Content-Security-Policy
CSP is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft, to site defacement, to malware distribution.
How to configure Security Header CSP in NGINX
Add this line below to the server block, Once you’re done, save your changes and reload Nginx.
add_header Content-Security-Policy "default-src 'self';" always;
The Nginx config would look like this, in my case I use Nginx as a load balancer on my local. check out my blog How to configure Nginx as a load balancer
upstream portal {
server localhost:8080;
}
server {
listen 80;
server_name portal.test;
add_header Content-Security-Policy "default-src 'self';" always;
location / {
proxy_pass http://portal;
}
}
X-XSS-Protection
X-XSS is also known as a Cross-Site Scripting header is used to defend against Cross-Site Scripting attacks. XSS Filter is enabled by default in modern web browsers such as Chrome, IE, and Safari. This header stops pages from loading when they detect reflected cross-site scripting (XSS) attacks.
You can implement XSS protection using the three options depending on the specific need.
- X-XSS-Protection: 0: This will disable the filter entirely.
- X-XSS-Protection: 1: This will enable the filter but only sanitizes potentially malicious scripts.
- X-XSS-Protection: 1; mode=block: This will enable the filter and completely blocks the page.
How to configure Security Header XSS-Protection in NGINX
To enable the X-XSS-Protection header in your Nginx Web Server, add the following line in your config file, Once you’re done, save your changes and reload Nginx.
add_header X-XSS-Protection "1; mode=block";
The Nginx config would look like this,
upstream portal {
server localhost:8080;
}
server {
listen 80;
server_name portal.test;
# Security header to prevent XSS attacks
add_header X-XSS-Protection "1; mode=block";
location / {
proxy_pass http://portal;
}
}
To verify that use curl -I so see all request headers
X-Frame-Options
The X-Frame-Options header is used to defend your website from clickjacking attacks by disabling iframes on your site. Currently, it is supported by all major web browsers. With this header, you tell the browser not to embed your web page in frame/iframe.
There are three ways to configure X-Frame-Options:
- DENY: This will disable iframe features completely.
- SAMEORIGIN: iframe can be used only by someone of the same origin.
- ALLOW-FROM: This will allow pages to be put in iframes only from specific URLs.
To enable the X-Frame-Options header in your Nginx Web Server, add the following line in your config file, Once you’re done, save your changes and reload Nginx.
add_header X-Frame-Options "DENY";
The Nginx config would look like this,
upstream portal {
server localhost:9004;
}
server {
listen 80;
server_name portal.test;
# Security header to prevent clickjacking
add_header X-Frame-Options "DENY";
location / {
proxy_pass http://portal;
}
}
To verify if the header is working, I’ve created an index.html with an iframe tag that loads my domain http://portal.test.
<!DOCTYPE html>
<html>
<head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>medium omarelfarsaoui</title>
</head>
<body><iframe src="http://portal.test"></body>
</html>
then open the index.html file into the browser. and check DevTools.
X-Content-Type-Options
If a response specifies an incorrect content type then browsers may process the response in unexpected ways. If the content type is specified to be a renderable text-based format, then the browser will usually attempt to interpret the response as being in that format, regardless of the actual contents of the response. Additionally, some other specified content types might sometimes be interpreted as HTML due to quirks in particular browsers. This behaviour might lead to otherwise “safe” content such as images being rendered as HTML, enabling cross-site scripting attacks in certain conditions.
To enable the X-Content-Type-Options
header in your Nginx Web Server, add the following line in your config file, Once you’re done, save your changes and reload Nginx.
add_header X-Content-Type-Options "nosniff"
The Nginx config would look like this,
upstream portal {
server localhost:9004;
}
server {
listen 80;
server_name portal.test;
# Security header
add_header X-Content-Type-Options "nosniff";
location / {
proxy_pass http://portal;
}
}
Access-Control-Allow-Origin
Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources. CORS also relies on a mechanism by which browsers make a “preflight” request to the server hosting the cross-origin resource, in order to check that the server will permit the actual request. In that preflight, the browser sends headers that indicate the HTTP method and headers that will be used in the actual request.
To enable the CORS
header in your Nginx Web Server, add the following line in your config file, Once done, save your changes and reload Nginx.
add_header 'Access-Control-Allow-Origin' 'https://foo.example' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
The Nginx config would look like this,
upstream portal {
server localhost:9004;
}
server {
listen 80;
server_name portal.test;
# Add CORS headers
add_header 'Access-Control-Allow-Origin' 'https://foo.example' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
location / {
proxy_pass http://portal;
}
}
Let’s verify that using my friend curl command
Conclusion
configuring security headers in Nginx is a critical step in safeguarding your web application from common vulnerabilities and attacks. By properly implementing headers like X-Frame-Options
, X-XSS-Protection
, Strict-Transport-Security
, and Content-Security-Policy
, you can significantly enhance the security posture of your site. These headers help protect against threats such as cross-site scripting (XSS), clickjacking, and insecure connections. Remember, while security headers provide an important layer of defense, they should be part of a broader security strategy, including proper SSL/TLS configurations, regular software updates, and continuous monitoring of your infrastructure. With the right configuration, you can ensure a more secure browsing experience for your users and build trust in your application
If you found this article on configuring security headers in Nginx valuable and want to keep up with the latest tips and best practices in web security, DevOps, and performance optimization, consider subscribing to our newsletter!
References:
- https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/
- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
- https://content-security-policy.com/examples/nginx/
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
- https://portswigger.net/kb/issues/00800400_content-type-incorrectly-stated