Best Practices for Nginx default_server Configuration and Public IP Handling with SSL Optimization


2 views

When deploying Nginx on Debian, the default configuration in /etc/nginx/sites-available/default creates a potential security exposure. The current behavior where IP address access falls through to your production domain creates multiple issues:

  1. Security warnings from SSL testers
  2. Potential duplicate content SEO penalties
  3. Improper SSL certificate handling

For production environments, we should create an explicit catch-all server block that rejects invalid requests:

# /etc/nginx/sites-available/default
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    
    ssl_certificate /path/to/dummy.crt;
    ssl_certificate_key /path/to/dummy.key;
    
    server_name _;
    return 444; # Close connection without response
}

Your production domain should have its own dedicated configuration without default_server designation:

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    
    server_name example.com www.example.com;
    
    include snippets/ssl-example.com.conf;
    include snippets/ssl-params.conf;
    
    # Rest of your production configuration
    ...
}

The solution achieves several security benefits:

  • Prevents IP-based access to your production site
  • Eliminates SSL warnings for direct IP access
  • Maintains full SSL security for your actual domain
  • Properly handles HTTP to HTTPS redirects

For the default_server SSL configuration, you'll need a self-signed certificate:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout /etc/nginx/ssl/dummy.key \
    -out /etc/nginx/ssl/dummy.crt \
    -subj "/CN=invalid"

After implementing these changes, verify with:

nginx -t # Test configuration
systemctl reload nginx # Apply changes
curl -I http://your_server_ip # Should show closed connection
curl -I https://your_server_ip # Should show closed connection
curl -I http://example.com # Should show 301 redirect
curl -I https://example.com # Should show 200 OK

When setting up Nginx on Debian, you'll encounter the default configuration in /etc/nginx/sites-available/default that serves the Nginx welcome page. This becomes problematic when you need to properly handle IP-based access and implement proper SSL termination.

The current configuration has these characteristics:

# Default server block (problematic)
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    
    # This catches all unmatched domain requests
    server_name _;
    
    root /var/www/html;
    index index.html;
    
    location / {
        return 403; # Forbidden access
    }
}

For your production domain (example.com), you should implement this structure:

# HTTP to HTTPS redirect
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

# SSL Termination
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com www.example.com;
    
    # SSL configuration
    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;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    
    # Your actual application configuration
    root /var/www/example.com;
    index index.php index.html;
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    # PHP processing
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    }
}

To properly block direct IP access while maintaining SSL functionality:

# Block all HTTP IP access
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 444; # No response (connection closed)
}

# Block all HTTPS IP access
server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    server_name _;
    
    # Use a self-signed cert for the default server
    ssl_certificate /etc/nginx/ssl/dummy.crt;
    ssl_certificate_key /etc/nginx/ssl/dummy.key;
    
    return 444;
}

For the IP-blocking SSL server, create a self-signed certificate:

sudo mkdir -p /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout /etc/nginx/ssl/dummy.key \
    -out /etc/nginx/ssl/dummy.crt \
    -subj "/CN=localhost"

After implementing these changes:

  1. Test SSL configuration: sudo nginx -t
  2. Reload Nginx: sudo systemctl reload nginx
  3. Verify behavior:
    • HTTP IP access should immediately close connection
    • HTTPS IP access should immediately close connection
    • HTTP domain access should redirect to HTTPS
    • HTTPS domain access should work normally
  4. Re-run SSL Labs test to confirm A+ rating

Key takeaways for production deployment:

  • Always specify default_server explicitly
  • Use separate server blocks for HTTP and HTTPS
  • Block IP access at both protocol levels
  • Maintain proper SSL configuration for your domain
  • Regularly test your SSL configuration with tools like SSL Labs