Securing Web Servers - KulbirJ/Protecting-the-Web- GitHub Wiki
In today’s digital landscape, securing web servers is critical to protect sensitive data, mitigate vulnerabilities, and align with zero trust security principles like "never trust, always verify." This article provides detailed, step-by-step configurations for three popular web servers—Microsoft Internet Information Services (IIS), NGINX, and Apache—to enable HTTPS and implement all modern security headers. These setups are based on the latest stable versions as of February 25, 2025: IIS 10 (Windows Server 2022), NGINX 1.24.x/1.25.x, and Apache 2.4.58/2.4.59. Whether you’re securing a corporate website, an application, or a service, this guide ensures a robust, secure foundation. With these configurations in place your vulnerability scans against OWASP Top 10 will come clean.
HTTPS encrypts traffic between clients and servers, preventing interception, while security headers mitigate common web vulnerabilities like cross-site scripting (XSS), clickjacking, and MIME-type sniffing. Together, they reduce attack surfaces and enforce strict access policies, key tenets of zero trust.
HTTPS: Requires a valid SSL/TLS certificate (e.g., from Let’s Encrypt or a trusted CA).
Security Headers: Includes HSTS, CSP, X-Content-Type-Options, and more, applied consistently across all servers.
Environment: Configurations assume modern OS platforms (Windows Server 2022 for IIS, Ubuntu 22.04/CentOS 9 for NGINX and Apache).
- OS: Windows Server 2022 with IIS 10.
- Certificate: Valid SSL/TSL certificate installed.
- Access: Administrative privileges.
- Install IIS:
Open Server Manager > Add Roles and Features > Select Web Server(IIS) > Install with defaults plus HTTP Redirection.
- Install SSL Certificate:
In IIS Manager (inetmgr), select the server node > Server Certificates > Import or Create Certificate Request. Example: Use Certify The Web for Let’s Encrypt automation.
- Configure HTTPS Binding:
Right-click your site (e.g., "Default Web Site") > Edit Bindings > Add: Type: https Port: 443 Host name: yourdomain.com SSL Certificate: Select imported certificate.
- Redirect HTTP to HTTPS:
Select site > HTTP Redirect > Check Redirect requests > Set to https://yourdomain.com/$1 > Status code: 301.
Edit or create web.config in your site’s root (e.g., C:\inetpub\wwwroot):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="X-Content-Type-Options" value="nosniff" />
<add name="X-Frame-Options" value="DENY" />
<add name="Content-Security-Policy" value="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none';" />
<add name="X-XSS-Protection" value="1; mode=block" />
<remove name="X-Powered-By" />
<add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains; preload" />
<add name="Referrer-Policy" value="strict-origin-when-cross-origin" />
<add name="Cache-Control" value="no-store, no-cache, must-revalidate" />
<add name="Pragma" value="no-cache" />
<add name="Permissions-Policy" value="geolocation=(), microphone=(), camera=()" />
</customHeaders>
</httpProtocol>
<rewrite>
<rules>
<rule name="HTTP to HTTPS Redirect" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="off" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering removeServerHeader="true" />
</security>
</system.webServer>
</configuration>
- TLS Hardening: Use IIS Crypto to disable SSL 2.0/3.0, TLS 1.0/1.1, enable TLS 1.2/1.3, and prefer strong ciphers (e.g., AES-256-GCM).
- Directory Browsing: Disable via Directory Browsing in IIS Manager.
- Request Filtering: Deny unused file extensions (e.g., .config) and verbs (e.g., TRACE).
- Restart IIS: iisreset
- Verify: https://yourdomain.com and SecurityHeaders.com.
- TLS Check: SSL Labs (aim for A+).
- OS: Ubuntu 22.04 or CentOS 9.
- Certificate: Valid SSL/TSL certificate installed.
- Access: Root or sudo privileges.
- Install NGINX:
Ubuntu:
sudo apt install nginx
CentOS:
sudo dnf install nginx
- Install SSL Certificate:
User Certbob:
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
- Configure Server Block:
Edit
/etc/nginx/sites-available/yourdomain.com
(Ubuntu) or/etc/nginx/conf.d/yourdomain.com.conf
(CentOS):
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
root /var/www/yourdomain.com/html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
Enable (Ubuntu):
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
Test:sudo nginx -t
Reload:sudo systemctl reload nginx
Update the HTTPS server block:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none';" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header Pragma "no-cache";
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
server_tokens off;
root /var/www/yourdomain.com/html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
- TLS Hardening
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384";
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
-
Directory Listing:
autoindex off;
-
Limit Methods:
location / {
limit_except GET POST { deny all; }
}
- Reload:
sudo systemctl reload nginx
- Verify:
https://yourdomain.com
and SecurityHeaders.com. - TLS Check: SSL Labs.
- OS: Ubuntu 22.04 or CentOS 9.
- Certificate: Valid SSL/TSL certificate installed.
- Access: Root or sudo privileges.
- Install Apache
Ubuntu:
sudo apt install apache2
CentOS:sudo dnf install httpd
- Enable Modules:
sudo a2enmod ssl headers rewrite
- Install SSL Certificate:
Use Certbot:
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
- Configure Virtual Host:
Edit
/etc/apache2/sites-available/yourdomain.com.conf
(Ubuntu) or/etc/httpd/conf.d/yourdomain.com.conf
(CentOS):
<VirtualHost *:80>
ServerName yourdomain.com
ServerAlias www.yourdomain.com
Redirect permanent / https://yourdomain.com/
</VirtualHost>
<VirtualHost *:443>
ServerName yourdomain.com
ServerAlias www.yourdomain.com
DocumentRoot /var/www/yourdomain.com/html
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/yourdomain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/yourdomain.com/chain.pem
</VirtualHost>
- Enable (Ubuntu):
sudo a2ensite yourdomain.com.conf
- Reload:
sudo systemctl reload apache2
(Ubuntu) orhttpd
(CentOS)
Update the HTTPS <virtualHost>
:
<VirtualHost *:80>
ServerName yourdomain.com
ServerAlias www.yourdomain.com
Redirect permanent / https://yourdomain.com/
</VirtualHost>
<VirtualHost *:443>
ServerName yourdomain.com
ServerAlias www.yourdomain.com
DocumentRoot /var/www/yourdomain.com/html
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/yourdomain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/yourdomain.com/chain.pem
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "DENY"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none';"
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Cache-Control "no-store, no-cache, must-revalidate"
Header always set Pragma "no-cache"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
ServerSignature Off
ServerTokens Prod
</VirtualHost>
- TLS Hardening
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder on
SSLSessionTickets off
- Directory Listing
<Directory /var/www/yourdomain.com/html>
Options -Indexes
</Directory>
- Limit Methods:
RewriteEngine On
RewriteCond %{REQUEST_METHOD} !^(GET|POST)
RewriteRule .* - [R=405,L]
Reload:
sudo systemctl reload apache2
(Ubuntu) orhttpd
(CentOS) Verify:https://yourdomain.com
and SecurityHeaders.com. TLS Check: SSL Labs
- X-Content-Type-Options: nosniff - Prevents MIME-type sniffing.
- X-Frame-Options: DENY - Blocks iframe embedding (clickjacking protection).
- Content-Security-Policy (CSP) - Limits resource sources to mitigate XSS; customizable for app needs.
- X-XSS-Protection: 1; mode=block - Legacy XSS filter for older browsers.
- Strict-Transport-Security (HSTS) - Enforces HTTPS for 1 year with preload option.
- Referrer-Policy - Controls referrer data for privacy.
- Cache-Control/Pragma - Prevents caching of sensitive content.
- Permissions-Policy - Restricts browser features (e.g., geolocation).
- Server Hiding - Removes server version banners.
This guide equips IIS, NGINX, and Apache with HTTPS and comprehensive security headers, ensuring a secure web presence as of February, 2025. Each configuration is adaptable to static or dynamic sites, with room to customize based on specific needs. By implementing these setups, you align with zero trust principles, enhancing security in an increasingly distributed world.