Nginx redirecting to the wrong site; poorly formed server_name directive - nginx

The Problem
When you type example.com into the address bar of a browser WITHOUT entering the scheme, i.e. http:// or https://, Nginx redirects the user to https://api.example.com instead of https://example.com as intended. I'm pretty sure there's something wrong with my Nginx config, but I'm not sure what.
Details
I'm hosting two websites on the same server, with the same IP. The relevant bits from the DNS zone file looks something like (domain and IP anonymized here):
example.com. 1800 IN A xxx.xxx.xxx.xxx
www.example.com. 1800 IN CNAME example.com.
api.example.com. 1800 IN CNAME example.com.
I have two SSL certs installed (provided by letsencrypt), one for each site, and both sites are configured to redirect to HTTPS. I have two vhost config files, one for each site, as follows:
/etc/nginx/sites-available/api
/etc/nginx/sites-available/default
Both are symlinked into /etc/nginx/sites-enabled/. The relevant bits from the two config files are as follows:
# /etc/nginx/sites-available/api
server {
listen 80;
listen [::]:80 ipv6only=on;
server_name api.example.com;
return 301 https://api.example.com$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
include snippets/api-ssl-params.conf; # ssl config info
server_name api.example.com;
# ... the rest of the site config ...
}
and:
# /etc/nginx/sites-available/default
server {
listen 80;
listen [::]:80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2 default_server;
listen [::]:443 default_server ipv6only=on;
include snippets/ssl-params.conf; # ssl config info
server_name example.com;
# ... the rest of the site config ...
}
I don't understand why just entering example.com into the address bar would redirect to https://api.example.com because:
just plain example.com doesn't appear in the api config file anywhere
example.com shouldn't match the server_name directive api.example.com
the server blocks in default are marked as default_server so shouldn't that take precedence when an ambiguous domain name was typed in?
Thanks!!!

Duh. Figured it out in the process of writing the question. The problem is that just plain example.com doesn't appear in the server_name directive for either of the sites listening on port 80. Since that causes ambiguity, nginx picks the first site in alphabetic order.
I updated the config file for the default site as follows:
# /etc/nginx/sites-available/default
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com; # <-- CHANGED THIS LINE
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2 default_server;
listen [::]:443 default_server ipv6only=on;
include snippets/ssl-params.conf; # ssl config info
server_name example.com;
# ... the rest of the site config ...
}
And all was right with the universe.

Related

DNS_PROBE_FINISHED_NXDOMAIN when serving multiple subdomains with nginx

On our research project, we have an Ubuntu 20.04 LTS virtual machine running, which should serve via nginx multiple project related websites/apps on different subdomains.
The setup is supposed to be as following:
maindomain --> redirecting to our project info site hosted by our university
subdomain1.maindomain --> nextcloud for project management stuff served via nginx
subdomain2.maindomain --> serving app1 via nginx proxy and gunicorn (for django)
subdomain3.maindomain --> serving app2 via nginx proxy and express.js
What I did:
Added the IP address of the server to the A record of our German domain hoster Strato.
https://maindomain: configured nginx to redirect to university site
server {
listen 80;
listen [::]:80;
listen 443 ssl http2;
listen [::]:443 ssl http2;
root /var/www/html;
server_name maindomain;
return 301 https://university-site;
ssl_certificate /etc/ssl/wildcard.crt;
ssl_certificate_key /etc/ssl/wildcard.key;
ssl_trusted_certificate /etc/ssl/wildcard.crt;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
}
configured nginx to serve nextcloud stuff in /etc/nginx/sites-available/subdomain1.maindomain
server {
listen 80;
#listen [::]:80;
server_name subdomain1.maindomain;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
root /var/www/nextcloud;
index index.html index.php /index.php$request_uri;
server_name subdomain1.maindomain;
ssl_certificate /etc/ssl/wildcard.crt;
ssl_certificate_key /etc/ssl/wildcard.key;
ssl_trusted_certificate /etc/ssl/wildcard.crt;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
... lot's of nextcloud related stuff ...
}
up to this point: everything is working fine for some time now
What I tried:
Now it was time to start deploying the next app on subdomain2.maindomain. In my naive thinking I thought just to copy /etc/nginx/sites-available/subdomain1.maindomain to subdomain2.maindomain and change "subdomain1.maindomain" to "subdomain2.maindomain" in the config file (of course: getting rid of all the nextcloud stuff, too).
First I experimented with just serving a static index.html page to see if everything is working:
server {
listen 80;
server_name subdomain2.maindomain;
root /var/www/subdomain2;
index index.html index.php /index.php$request_uri;
# return 301 https://$server_name$request_uri;
}
added a static index.html into /var/www/subdomain2
restarted nginx (feels like a 1000 times actually... :-))
Now when I try to navigate to "http://subdomain2.maindomain" it throws the error: DNS_PROBE_FINISHED_NXDOMAIN
For testing purposes I added our IP address to the configuration /etc/nginx/sites-available/subdomain2.maindomain, resulting in:
server {
listen 80;
server_name subdomain2.maindomain IP_ADDRESS;
root /var/www/subdomain2;
index index.html index.php /index.php$request_uri;
# return 301 https://$server_name$request_uri;
}
Now, when I browse to IP_ADDRESS the static index.html page is served just as expected, but browsing to subdomain2.maindomain still fails.
What can I do next?

Nginx redirects subdomain foo to www.foo to www.www.foo etc

I just changed my config from a regular to a wildcard certificate. Now my nginx is misbehaving.
# redirect http to https
server {
listen 80 default_server;
listen [::]:80 default_server;
return 301 https://$host$request_uri;
}
# redirect naked to www
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
include ssl.conf;
return 301 https://www.$host$request_uri;
}
# serve subdomain www
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.example.com;
include ssl.conf;
# ...
}
# serve subdomain mmm
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name mmm.example.com;
include ssl.conf;
# ...
}
# ...etc.
The above works, but fails for non-existent subdomains (instead of returning 404). So if I try notexist.example.com it will redirect me to www.notexist.example.com and give me a certificate warning. If I click ok, it will redirect to www.www.notexist.example.com, and then www.www.www.notexist.example.com, etc.
What am I doing wrong?
Since you want to catch all non-existent subdomains you need an extra server block at the end, marked as default - like listen 443 ssl default_server; The server_name for this block does not matter - as long as it does not match any of the other server blocks (so you can simply use server_name _;)
Any domain that is not already handled by another server block will be handled by the default one - you can either redirect to your canonical domain or just return 404.

Nginx - Generic redirect all www to non-www

Is it possible to make a generic HTTP -> HTTPS and WWW -> non-WWW redirect in Nginx which impact all domains. Or do I need to set it up for every single domain?
I've made the HTTP->HTTPS as follows:
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
You should use two server blocks for this:
server { # redirect http/https www.example.com to https://example.com
listen 80;
listen 443 ssl;
server_name www.example.com;
ssl_certificate <path_to_ssl_cert_for_domain_www.example.com>;
ssl_certificate_key <path_to_key_for_ssl_cert_above>;
# other ssl parameters here if needed
return 301 https://example.com$request_uri;
}
server { # redirect http://example.com to https://example.com
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
server { # main site config
listen 443 ssl;
server_name example.com;
# rest of your configuration here
...
}
Usually paid SSL certificates already includes both domain name with www prefix and without it. If you're using Lets Encrypt service, you can generate certificate for both example.com and www.example.com by yourself.

nginx setup WordPress multiple domains and subdomains

Anyone can point me please, in setting up nginx correctly.
I have 3 domains and 1 subdomain, and I have problem with only one domain.
All domains have main server block the same
domain1.com
server {
# Server host
server_name domain1.com *.domain1.com; # Other domains are set to 1.com, 2.com ...
# Server ports
listen 80;
listen [::]:80 ipv6only=on;
listen 443 ssl http2;
listen [::]:443 ipv6only=on ssl http2;
Now funny thing is this:
$ curl -I http://www.domain2.com,
HTTP/1.1 200 OK
Link: <http://www.domain3.com/>; rel=shortlink
$ curl -I https://domain2.com
HTTP/1.1 302 Found
Location: https://www.domain1.com
So non-www https redirects to domain1.com, and www http displays domain3.com.
I have tried to add:
server {
listen 80;
listen [::]:80;
server_name www.domain2.com, domain2.com;
return 301 https://www.domain2.com$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name domain2.com;
return 301 https://www.domain2.com$request_uri;
}
But this doesn't helps.
I see that probably some settings are redundant but not sure why is this not working.
What I want to achieve is:
domain2.com, www.domain2.com > https://www.domain2.com
I also have a subdomain here, but that one is working well: dev.domain2.com
I think it could have to do with you separating the server names by a comma instead of just a space. Try removing the comma in this line and see if you have any luck?
server_name www.domain2.com, domain2.com;

Nginx server_name & listen matches specified patterns

In this example the domain has been replaced with domain.com
Our main issue:
When i type https://domain.com i don't get redirected to https://www.domain.com, we currently don't have a rule for this what would be the best way to solve this?
According to our nginx configuration we have not specified 443 for https://domain.com but still its accessible, why is that?
We have valid ssl certificates for both domain.com and www.domain.com.
We do not have a wildcard certificate *.domain.com.
Our Configuration:
#All non-matching patterns
server
{
listen 80;
#enabling this will cause things to break.
#2015/12/18 09:21:54 [error] 32165#0: *1661 no "ssl_certificate" is defined in server listening on SSL port while SSL handshaking, client: *censored*, server: 0.0.0.0:443
#listen 443 ssl;
#Horrible looking match all pattern.
server_name _ "" domain.com *.domain.com;
return 301 https://www.domain.com$request_uri;
}
#Main site ssl enforced
server
{
listen 443 ssl;
server_name www.domain.com ios.domain.com android.domain.com;
...
}
#Staging / Test site
server
{
listen 443 ssl;
listen 80;
server_name stage.domain.com;
...
}
#Rental cars site ssl enforced
server
{
listen 443 ssl;
server_name hyrbil.domain.com;
...
}
#ios redirect to enforce https
server
{
listen 80;
server_name ios.domain.com;
return 301 https://ios.domain.com$request_uri;
}
#android redirect to enforce https
server
{
listen 80;
server_name android.domain.com;
return 301 https://android.domain.com$request_uri;
}
Bonus question:
Is it possible to match all ssl traffic and do a redirect unless it matches a specific domain, for example make https://xxx.domain.com pass a 301 to https://www.domain.com even tho i don't have a certificate for xxx.domain.com without showing "This page is unsecure, are you sure that you want to proceeed"?
If you have one virtualhost listening on 443, all traffic reaches your IP address will be served by that virtualhost.
Create an SSL virtualhost for domain.com and put a simple redirect in it.
Or create a "catch all/default" SSL virtualhost, and check the HOST header and redirect regarding that, like:
if ($host !~* ^www\.doman\.com$) {
rewrite ^(.*)$ http://www.domain.com$1 permanent;
}
But it will show SSL certificate error on all FQDNs not included in your certificate!

Resources