Many developers find themselves maintaining nearly identical Nginx server {}
blocks for HTTP (port 80) and HTTPS (port 443) configurations. This leads to code duplication and maintenance headaches:
# Bad - Duplicated configuration
server {
listen 80;
server_name example.com;
root /var/www/html;
index index.html;
# 20 more directives...
}
server {
listen 443 ssl;
server_name example.com;
root /var/www/html;
index index.html;
# Same 20 directives repeated
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
}
The most maintainable approach is to use Nginx's include
directive to share common configuration:
# /etc/nginx/common.conf
root /var/www/html;
index index.html;
# All shared directives go here
# /etc/nginx/sites-enabled/example.com
server {
listen 80;
server_name example.com;
include common.conf;
}
server {
listen 443 ssl;
server_name example.com;
include common.conf;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
}
For simpler cases where you just need to conditionally enable SSL:
map $scheme $ssl_flag {
https 'on';
default 'off';
}
server {
listen 80;
listen 443 ssl;
server_name example.com;
ssl $ssl_flag;
# Shared config here
if ($ssl_flag = 'on') {
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
}
}
When merging configurations:
- Test thoroughly - some directives behave differently in HTTP vs HTTPS
- Watch for mixed content warnings when serving assets
- Consider using HTTP/2 for HTTPS connections
- Keep security headers consistent across protocols
Here's how we implemented this on a production site:
# common_config.conf
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
root /var/www/production;
index index.php index.html;
error_log /var/log/nginx/example_error.log;
access_log /var/log/nginx/example_access.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# HTTPS server
server {
listen 443 ssl http2;
server_name example.com;
include common_config.conf;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
}
# HTTP server
server {
listen 80;
server_name example.com;
include common_config.conf;
return 301 https://$host$request_uri;
}
When setting up both HTTP and HTTPS versions of a website in Nginx, you'll often find yourself writing nearly identical server
blocks. The only differences typically being the port (80 vs 443) and SSL-related directives. This leads to configuration duplication that's hard to maintain:
server {
listen 80;
server_name example.com;
root /var/www/html;
# ... other common directives ...
}
server {
listen 443 ssl;
server_name example.com;
root /var/www/html;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# ... same common directives as above ...
}
The most maintainable approach is to extract the common configuration into a separate file and include it in both server blocks:
# common.conf
root /var/www/html;
index index.html;
# ... other shared directives ...
# nginx.conf
server {
listen 80;
server_name example.com;
include common.conf;
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
include common.conf;
}
For simpler cases, you can use a single server block that handles both protocols:
server {
listen 80;
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# Common configuration
root /var/www/html;
# ... other directives ...
}
If you need different behavior based on the protocol, use the $scheme
variable:
location /secure-area {
if ($scheme = http) {
return 301 https://$host$request_uri;
}
# HTTPS-only configuration
}
- Use includes for complex configurations
- Keep SSL-specific directives in the HTTPS block
- Consider HTTP/2 for better performance
- Always redirect HTTP to HTTPS for security
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}