How to Exclude Specific Paths from SSL in Nginx Configuration


2 views

When configuring HTTPS for a website, there might be scenarios where you need to exclude certain paths from SSL enforcement. This could be for backward compatibility, third-party integrations, or specific performance optimizations. Here's how to handle this in Nginx.

Here's a typical Nginx SSL configuration that forces HTTPS for all requests:

server {
    listen 443 ssl;
    
    ssl_certificate /path/to/your/certificate.crt;
    ssl_certificate_key /path/to/your/private.key;
    
    server_name example.com;
    
    location / {
        proxy_pass http://localhost:8000;
    }
}

To exclude paths starting with /foo/ from SSL while keeping the rest of the site secure, we need to:

  1. Set up a separate HTTP server block
  2. Redirect all HTTPS traffic except the excluded paths

Here's the complete configuration that accomplishes this:

# HTTPS Server - handles all requests except /foo/
server {
    listen 443 ssl;
    
    ssl_certificate /path/to/your/certificate.crt;
    ssl_certificate_key /path/to/your/private.key;
    
    server_name example.com;
    
    # Main proxy pass
    location / {
        proxy_pass http://localhost:8000;
    }
    
    # Redirect /foo/ to HTTP
    location ^~ /foo/ {
        return 301 http://$host$request_uri;
    }
}

# HTTP Server - handles only /foo/ requests
server {
    listen 80;
    server_name example.com;
    
    location ^~ /foo/ {
        proxy_pass http://localhost:8000;
    }
    
    # Redirect all other requests to HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}
  • The ^~ modifier makes these locations priority matches
  • Session cookies set on HTTPS won't be accessible on HTTP pages
  • Mixed content warnings may occur if resources are loaded via HTTPS
  • Consider security implications for any data transmitted over HTTP

For more complex scenarios, you might use a map directive:

map $uri $ssl_required {
    ~^/foo/ 0;
    default 1;
}

server {
    listen 80;
    server_name example.com;
    
    if ($ssl_required) {
        return 301 https://$host$request_uri;
    }
    
    location /foo/ {
        proxy_pass http://localhost:8000;
    }
}

This approach gives you more flexibility in defining SSL requirements based on various conditions.


When running a production website, you typically want to enforce HTTPS for security. However, there might be specific legacy endpoints or third-party integrations that require HTTP access. In Nginx, this requires careful configuration to avoid mixed-content issues while maintaining security.

We'll use two server blocks - one for HTTPS (port 443) and another for HTTP (port 80). The HTTP server will only handle the specific paths we want to exclude from SSL, while redirecting all other traffic to HTTPS.

# HTTPS server - handles most requests
server {
    listen 443 ssl;
    server_name www.mydomain.com;

    ssl_certificate /home/admin/ssl/ssl.crt;
    ssl_certificate_key /home/admin/ssl/ssl.key;

    # Main proxy configuration
    location / {
        proxy_pass http://localhost:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # Special case: Redirect /foo/ requests to HTTP
    location ^~ /foo/ {
        return 301 http://$host$request_uri;
    }
}

# HTTP server - only handles /foo/ requests
server {
    listen 80;
    server_name www.mydomain.com;

    # Only process /foo/ requests
    location ^~ /foo/ {
        proxy_pass http://localhost:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # Redirect everything else to HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}
  • The ^~ prefix in location blocks ensures exact path matching
  • HTTP to HTTPS redirection uses 301 (permanent) for SEO benefits
  • Both server blocks maintain consistent proxy headers
  • SSL configuration only appears in the HTTPS server block

After implementing this configuration:

  1. Test with curl -I https://www.mydomain.com/foo/bar - should return HTTP 301 redirect
  2. Test with curl -I http://www.mydomain.com/foo/bar - should return HTTP 200
  3. Test with curl -I http://www.mydomain.com/other - should redirect to HTTPS

When implementing this pattern:

  • Ensure no sensitive data is transmitted via the unencrypted paths
  • Consider adding HSTS headers with appropriate max-age and includeSubDomains
  • Monitor mixed-content warnings in browser consoles

If you prefer to handle everything in a single server block, you can use this conditional approach:

server {
    listen 443 ssl;
    server_name www.mydomain.com;

    ssl_certificate /home/admin/ssl/ssl.crt;
    ssl_certificate_key /home/admin/ssl/ssl.key;

    location / {
        if ($request_uri ~ ^/foo/) {
            return 301 http://$host$request_uri;
        }
        proxy_pass http://localhost:8000;
    }
}

However, this is generally less performant than the two-server-block approach.