Skip to content

Separate HTTP/HTTPS server blocks needed for HSTS and proper redirect chains #62

Open

Description

@davidt-de

Issue: Separate HTTP/HTTPS server blocks needed for HSTS and proper redirect chains

Current Behavior

The current vhost template creates combined server blocks listening on both port 80 and 443, using if ($scheme != "https") for HTTP→HTTPS redirects. This causes two issues:

  1. NCSC/Internet.nl compliance tests fail for HTTP→HTTPS redirect detection
  2. HSTS headers cannot be set reliably on the first contact (before any redirect)

Current Template Structure (simplified)

server {
  listen 80;
  listen [::]:80;
  listen 443 ssl;
  # ... SSL config
  server_name www.example.com;
  return 301 https://example.com $request_uri;  # Redirects to different domain
}

server {
  listen 80;
  listen [::]:80;
  listen 443 ssl;
  # ... SSL config
  server_name example.com;
  
  if ($scheme != "https") {
    rewrite ^ https://$host$request_uri permanent;  # Same-block HTTPS redirect
  }
  # ... content
}

Expected Behavior

Per NCSC-NL guideline U/WA.05 and Internet.nl tests:

  1. Strict separation: One server block for HTTP (port 80 only), separate blocks for HTTPS (port 443 only)
  2. HSTS on every response, including redirect responses
  3. Redirect chain: HTTP domain → HTTPS same domain → HTTPS target domain (if canonicalization needed)

Proposed Template Structure

# === HTTP: All domains → HTTPS (same domain) ===
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com alias1.com www.alias1.com;
    
   return 301 https://$host$request_uri;
}

# === HTTPS: www.example.com → example.com (canonicalization) ===
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    # ... SSL config
    server_name www.example.com;
    
    add_header Strict-Transport-Security "max-age=300; includeSubDomains" always;
    return 301 https://example.com$request_uri;
}

# === HTTPS: alias1.com → example.com (domain consolidation) ===
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    # ... SSL config
    server_name alias1.com www.alias1.com;
    
    add_header Strict-Transport-Security "max-age=300; includeSubDomains" always;
    return 301 https://example.com$request_uri;
}

# === HTTPS: Main host (content) ===
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    # ... SSL config
    server_name example.com;
    
    add_header Strict-Transport-Security "max-age=300; includeSubDomains" always;
    
    # ... root, logs, locations
}

Template Variables Needed

Variable Purpose
{{server_name_main}} Primary domain (content server)
{{server_name_redirects}} List of domains that redirect to main (www, aliases)
{{hsts_max_age}} HSTS max-age value (default: 300, stages: 604800, 2592000, 31536000)
{{hsts_include_subdomains}} includeSubDomains directive (optional)

Use Cases

  • SEO canonicalization: www. → non-www or vice versa
  • Domain consolidation: Multiple TLDs (.com, .de) → primary domain
  • HSTS preload readiness: Proper header placement for hstspreload.org

References

Environment

  • CloudPanel version: 2.x
  • Web server: Nginx

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions