Setup HTTPS as default but allow HTTP? - ssl

I've an nginx vhost setup with both HTTP and HTTPS.
I'd like to make visitors which write in the address bar "www.example.com" to be directed to the HTTPS version of my website, but if they specify http://www.example.com then I'd like them to get the HTTP version.
I have tried setting the ssl listen as default_server but nothing seems different...
server {
listen *:80;
listen *:443 ssl spdy default_server;
ssl_certificate /etc/ssl/custom/www.example.com.crt;
ssl_certificate_key /etc/ssl/custom/www.example.com.key;
server_name example.com www.example.com;
}
Is there a way?

If the user specifies 'www.example.com' the browser simply assumes http://www.example.com. While you can redirect the http:// then to https:// there is no way to do this only if the access was caused by the browsers assumption, that is you cannot distinguish between the user entering http://... and the browser assuming http://...

Related

Nginx redirection conflicts with other ports

My situations is as follows:
app 1 running at: server.domain.com (192.168.1.3)
app 2 running at: server.domain.com:8080 (192.168.1.2)
My router is set up to route requests on port 80 to app 1 and port 8080 to app 2.
So far so good, this scenario has been working for ages.
Recently I tried switching to nginx and I decided to redirect http traffic to https traffic for app 1.
I set up a container with nginx and am using the following config:
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
# main server block
server {
listen 443 ssl default_server;
root /config/www;
index index.html index.htm index.php;
server_name _;
ssl_certificate /path to cert;
ssl_certificate_key /path to cert;
ssl_dhparam /path to cert;
ssl_ciphers '';
ssl_prefer_server_ciphers on;
client_max_body_size 0;
location / {
try_files $uri $uri/ /index.html /index.php?$args =404;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# With php7-cgi alone:
fastcgi_pass 127.0.0.1:9000;
# With php7-fpm:
#fastcgi_pass unix:/var/run/php7-fpm.sock;
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
}
}
This successfully redirects http to https and app 1 works as expected.
However when trying to visit app 2 I will also be redirected to https (which it shouldn't, app 2 doesn't support it)
Now I already figured out why this happens.
Google Chrome has a cache so when I visit the non-https url it gets a 301 redirect to the https version. It saves this in it's cache and now thinks I always want https regardless of the port.
The workaround I've found is going to chrome://net-internals and clearing the cache there. Opening app 2 then succeeds but after visiting app 1 I end up in the same loop all over again.
I've tried several default fixes found all over the net but none of them have worked thus far.
Anyone know what I have to put in my config to fix this?
ps: cert paths, domain names and ports are fake representations of the real situation
First off it would be helpful if in the nginx config you label which server definition corresponds to App 1 and App 2, because it appears there may be a mix up in the configuration. You are also missing some configuration, such as listening to port 8080. So first I'll clarify the requirements you clearly stated for both apps:
App 1:
Listens on port 80
Uses SSL
App 2:
Listens on port 8080
Does not use SSL / doesn't support it.
So I'd recommend config closer to:
# Corresponds better to app 2 given your requirements
server {
listen 8080 default_server;
server_name _;
# NOTE: You may want to listen for certain routes, without redirect EG
# location /foo/* { . . . }
return 301 $scheme://$host$request_uri;
}
# main server block - app 1
server {
listen 443 ssl default_server;
. . . # The rest of your definition here is fine for an SSL server
}
My main point here is that the server block on port 80 as you've defined it above is just a redirect machine to https, hardcoded. This block as you've defined it contradicts the requirements that you "route requests on port 80 to app 1" and you "use SSL for app 1" since your SSL configuration is actually in the second server definition. What you've set up in the first server definition is actually a pattern used to force ssl redirects leaving you in a position where you'll never serve non-ssl HTTP traffic. This might clear up the issue somewhat; perhaps I can help more once the server blocks more closely match the stated requirements.
Finally noting that it is possible to listen to multiple ports and route to http and https traffic within one server definition block:
server {
listen 80;
listen 443 ssl;
# can force some routes to be ssl or non ssl accordingly
}
Configuration like this may be more ideal if both app servers are hosted on the same machine using the same nginx service.

nginx would not react to wildcard subdomains (*.domain)

i have have trouble with nginx wildcard sub-domains.
I've bought a domain from godaddy. then create host zone in amazone and then created a record set for A(A-IPv4) - for both www.domain.com / domain.com
and in nginx.conf i have the very basic configuration and 1 server file that look like this:
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
access_log /var/log/nginx/domain.com.access.log;
error_log /var/log/nginx/domain.com.error.log;
# i tried the following for server name: domain.com *.domain.com/*.domain.com/.domain.com
server_name .domain.com;
root /usr/share/nginx/html/test;
index index.html;
}
when i visit subdomain.domain.com i get Firefox can't find the server at subdomain.domain.com
looking at the access log files nothing got logged it's completely empty.
Thanks
You have to add A-record for subdomains too. And before visiting site in Firefox, try ping - you will see if domain name can be resolved at all.

Nginx non HTTPS websites redirect to other HTTPS websites

I'm using nginx for hosting multiple websites on a ubuntu server. Basically my setup is as follows.
My first domain example1.com (SSL enabled) can be accessed from http://example1.com as well as from https://example1.com. This working fine.
But my 2nd domain example2.com, I don't have SSL enabled - but when I type https://example2.com the url redirects to the first domain https://example1.com - Which is wrong
Now currently I have added this server block if someone type in the URL with https:// it will redirect back to http:// on the same domain. But this is not the right way to handle this issue. Does anyone has some better ideas?
server {
listen 443 ssl;
server_name example2.com www.example2.com;
rewrite ^ http://$server_name$request_uri? permanent;
}
The problem here is that you’re only using a single IP address (server-side) and rely on the TLS Server Name Indication extension (client-side). Nginx will always use your default HTTPS server if nothing else is available to handle the request.
Your solution looks quite right to me, although it will produce an error on the client-side if you have no valid certificate. The only other possibility would be to create a default invalid HTTPS server that simply drops the connection attempt. But I guess that’s not what you want.
server {
listen 443 ssl;
server_name example2.com *.example2.com;
return 301 http://$server_name$request_uri;
}
Always use return if you redirect at such a point.
A default invalid catch all configuration could look like the following:
server {
listen 443 ssl;
server_name _;
ssl_certificate blank.crt;
ssl_certificate_key blank.key;
return 403;
}
As I said, it will simply drop any connection attempt that doesn't contain a valid HTTP Host in the submitted headers or if the submitted HTTP Host in the header is not valid.
Having the following will listen on 443 (SSL), and because you don't have a SSL certificate for this domain, nginx will use the first or default SSL certificate, which will throw invalid domain error. Simply remove it, so that it doesn't listen on 443 (SSL).
server {
listen 443 ssl;
server_name example2.com www.example2.com;
rewrite ^ http://$server_name$request_uri? permanent;
}

Nginx serving SSL certificate of another site

I'm serving two sites with Nginx. First site (say A) has a SSL certificate and second site (say B) doesn't. Site A works fine when opening on https and B on http. But when I access site B on https, nginx serves the SSL cert and contents of site A with domain of B, which shouldn't happen.
Nginx config for site A is as follows. For site B, it's just a reverse proxy to a Flask app.
server {
listen 80;
server_name siteA.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name siteA.com;
ssl_certificate /path/to/cert.cert
ssl_certificate_key /path/to/cert_key.key;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:RC4-SHA:AES256-GCM-SHA384:AES256-SHA256:CAMELLIA256-SHA:ECDHE-RSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:CAMELLIA128-SHA;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
keepalive_timeout 70;
# and then the `location /` serving static files
}
I can't figure out what is wrong here.
Apparently I need a dedicated IP for site A.
Quoting from What exactly does "every SSL certificate requires a dedicated IP" mean?
When securing some connection with TLS, you usually use the certificate to authenticate the server (and sometimes the client). There's one server per IP/Port, so usually there's no problem for the server to choose what certificate to use. HTTPS is the exception -- several different domain names can refer to one IP and the client (usually a browser) connects to the same server for different domain names. The domain name is passed to the server in the request, which goes after TLS handshake. Here's where the problem arises - the web server doesn't know which certificate to present. To address this a new extension has been added to TLS, named SNI (Server Name Indication). However, not all clients support it. So in general it's a good idea to have a dedicated server per IP/Port per domain. In other words, each domain, to which the client can connect using HTTPS, should have its own IP address (or different port, but that's not usual).
Nginx was listening on port 443 and when request for site B went on https, the TLS handshake took place and the certificate of site A was presented before serving the content.
The ssl_certificate parameter should be closed with ; to get expected output.
Also make sure that you have followed the correct syntax in all the config file parameters by using following command and then restart or reload the service:
sudo nginx -t
NGINX supports SNI, so it's possible to serve different domains with different certificates from the same IP address. This can be done with multiple server blocks. NGINX has documented this in
http://nginx.org/en/docs/http/configuring_https_servers.html
For me HTTP2 and IPv6 are important, so I to listen to [::] and set ipv6only=off. Apparently this option should only be set for the first server block, otherwise NGINX will not start.
duplicate listen options for [::]:443
These server blocks
server {
listen [::]:443 ssl http2 ipv6only=off;
server_name siteA.com www.siteA.com;
ssl_certificate /path/to/certA.cert
ssl_certificate_key /path/to/certA_key.key;
}
server {
listen [::]:443 ssl http2;
server_name siteB.com www.siteB.com;
ssl_certificate /path/to/certB.cert
ssl_certificate_key /path/to/certB_key.key;
}
If you host multiple sites in you server and in one Nginx config if you have listen 443 ssl http2 default_server;
The default_server will give the same cert to all domains. removing it will fix the problem.
While following this tutorial I total missed this part:
Note: You may only have one listen directive that includes the default_server modifier for each IP version and port combination. If you have other server blocks enabled for these ports that have default_server set, you must remove the modifier from one of the blocks.

Why does listen `443 default_server ssl` work for multiple server names in nginx?

I run nginx for static content and as a proxy to Apache/mod_wsgi serving django. I have example.com and test.example.com as proxy to Apache/Django and static.example.com which serves all static files directly through nginx. I have a wildcard SSL cert so that each of these sub-domains can use SSL (and I only have one IP).
Why is it that when using listen 443 default_server ssl; in either test.example.com or example.com, SSL works for both yet I have to explicitly listen to 443 for static.example.com?
ssl_certificate /etc/ssl/certs/example.chained.crt;
ssl_certificate_key /etc/ssl/private/example.key;
server {
listen 80;
listen 443;
server_name static.example.com;
# ... serves content ...
}
server {
listen 80;
listen 443 default_server ssl;
server_name example.com;
# ... proxy pass to http://example.com:8080 (apache) ...
}
server {
listen 80;
# why don't I need `listen 443;` here?
server_name test.example.com;
# ... proxy pass to http://test.example.com:8080 (apache) ...
}
The SSL protocol by itself (without the SNI extension) uses the ip address of the server to request the SSL certificate. With SNI it also passes the hostname (doesn't work for Win XP), but that should't be relevant here.
Server directives are not an exact match. It's the "closest" match. It may appear "work", but it may be ending up in the wrong server directive. It's hard to tell without any more information, like the server root.
The point is something will always work since you appear to be using a single ip address.