When running sensitive pages like signup forms, enforcing HTTPS isn't just recommended - it's mandatory. While modern browsers may automatically upgrade HTTP requests, we shouldn't rely on client-side behavior for security. Nginx provides server-side solutions that are more reliable and performant than framework-level redirects (like Rails plugins).
Here's how to modify your existing configuration to handle both HTTP and HTTPS traffic properly:
server {
listen 80;
server_name signup.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name signup.example.com;
ssl_certificate /path/to/my/cert;
ssl_certificate_key /path/to/my/key;
ssl_session_timeout 30m;
location / {
root /path/to/my/rails/app/public;
index index.html;
passenger_enabled on;
}
}
The first server block catches all HTTP traffic (port 80) and issues a permanent redirect (301) to the HTTPS version. Key components:
return 301
- Permanent redirect that browsers cache$host
- Preserves the original hostname$request_uri
- Maintains the full request path
For enhanced security, consider adding these SSL parameters to your HTTPS server block:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384...';
ssl_stapling on;
ssl_stapling_verify on;
After making changes, always test your Nginx configuration before applying:
sudo nginx -t
sudo systemctl reload nginx
Verify with curl to ensure proper redirection:
curl -I http://signup.example.com
For legacy systems or specific requirements, you might need alternative approaches:
# Alternative using rewrite
server {
listen 80;
server_name signup.example.com;
rewrite ^ https://$server_name$request_uri? permanent;
}
# If you need to preserve ports
server {
listen 80;
server_name signup.example.com;
return 301 https://$server_name:$server_port$request_uri;
}
When running a sensitive subdomain like signup.example.com
that should only be accessible via HTTPS, you might encounter situations where users accidentally access it via HTTP. This creates security concerns and poor user experience when they hit a 404 instead of being automatically redirected to the secure version.
The most efficient way to handle this is by adding a separate server block in your Nginx configuration that listens on port 80 (HTTP) and performs a 301 redirect to the HTTPS version. Here's how to implement it:
server {
listen 80;
server_name signup.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name signup.example.com;
ssl_certificate /path/to/my/cert;
ssl_certificate_key /path/to/my/key;
ssl_session_timeout 30m;
location / {
root /path/to/my/rails/app/public;
index index.html;
passenger_enabled on;
}
}
The first server block catches all HTTP traffic (port 80) and immediately issues a permanent redirect (301) to the HTTPS version. The $host
variable preserves the original domain name, and $request_uri
maintains the full request path.
For maximum security, you should also include these directives in your HTTPS server block:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256...';
ssl_stapling on;
ssl_stapling_verify on;
After making these changes:
- Test your Nginx configuration:
sudo nginx -t
- Reload Nginx:
sudo systemctl reload nginx
- Verify with curl:
curl -I http://signup.example.com
should show a 301 redirect
If you want to enforce HTTPS across your entire domain, you can use this more generic approach:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 301 https://$host$request_uri;
}