简体   繁体   中英

Redirecting all non-subdomain requests to (https)apex domain with Nginx

I have a fairly complex Ruby application that gives customers a dashboard available under their own chosen subdomain. ie: http://mycompany.app.com , http://myproject.app.com .

I also have the product website running on the root domain (ie, http://app.com ) and I've bought and configured an SSL certificate with Nginx and it's working as expected, but that leaves me with the following problem scenario:

I need to redirect all non-https traffic to the https version of the page, except for any requests going to any of the subdomains. What makes it tricky is that I do however need to redirect the www version of the site to the non-www version.

http://app.com             -> https://app.com
http://www.app.com         -> https://app.com
http://nike-admin.app.com !-> https://nike-admin.app.com

Here's what I have come up with so far in nginx.conf for this app (real name replaced by app ):

upstream unicorn_server {
  server unix:/var/www/<app>/tmp/sockets/unicorn.sock fail_timeout=0;
}

server {
  listen 80;
  listen [::]:80 default_server ipv6only=on;
  server_name <app>.co;

  location / {
    rewrite ^ https://$server_name$request_uri permanent;
  }
}

server {
  server_name <app>.co;
  root /var/www/<app>.co/public;

  client_max_body_size 4G;
  keepalive_timeout 70;
  listen 443 ssl;
  ssl_certificate /etc/nginx/ssl/<app>.crt;
  ssl_certificate_key /etc/nginx/ssl/<app>.key;

  location / {
    try_files $uri @app;
  }

  location @app {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://unicorn_server;
  }
}

In the first server{} block, I explicitly listen for any connection on port 80 and redirect those to the https version, but that's a double-edged sword as the SSL certificate only covers the apex domain. I don't want requests on any subdomains other than www to be redirected to the https equivalent.

I could potentially use regex, but from what I've seen online, but it seems to be frowned upon?

Is there any other way to do this?

You can use the below regex:

/^.*?\b(?!www\.)((?:[\w-]+\.)+[\w-]+).*$/

Replace with:

https://$1

Here is a regex demo !

  • ^ Asserts position at the start of string.
  • .*? Matches any number of characters except new line in lazy match. (Char by char)
  • \\b Asserts a word boundary position ( \\w\\W or \\W\\w .)
  • (?! Negative lookahead: Asserts that our position is NOT :
    • www\\. The character sequence "www." .
  • ) Then carry on the match:
  • ( Opens a capturing group . This is so we can use a back-reference in the replacement.
    • (?: Opens a non-capturing group.
      • [\\w-]+\\. Any sequence of word characters or hyphens (because hyphens may be in domain names) followed by a dot.
    • )+ One or more groups.
  • [\\w-]+ Another group of word characters or hyphens.
  • ) Closes the group.
  • .* Matches the rest of the string but it's not part of the domain, hence ignored.
  • $ Asserts position at end of string. This anchor may not be necessary, but it's good to have for readability.

Read more:

(http:\/\/(www\.)?)([a-zA-Z-]+\.)(?![a-zA-Z-]+\.)

This will match:

  1. Anything that uses http ; or
  2. Anything that starts with www .

This will NOT match:

  1. Anything that has a subdomain.

If you wanted to do a ruby replacement, here are some examples (in irb):

http://app.com (switches to https)

2.1.1 :047 > "http:\/\/app.com".sub /(http:\/\/(www\.)?)([a-zA-Z-]+\.)(?![a-zA-Z-]+\.)/, 'https://\3'
 => "https://app.com" 

http://www.app.com (switches to https)

2.1.1 :046 > "http:\/\/www.app.com".sub /(http:\/\/(www\.)?)([a-zA-Z-]+\.)(?![a-zA-Z-]+\.)/, 'https://\3'
 => "https://app.com" 

http://subdomain.app.com (stays the same)

2.1.1 :045 > "http:\/\/subdomain.app.com".sub /(http:\/\/(www\.)?)([a-zA-Z-]+\.)(?![a-zA-Z-]+\.)/, 'https://\3'
 => "http://subdomain.app.com"

I use this construction and it works fine:

server {
        listen 80;
        server_name my.site.ru www.my.site.ru;
        rewrite ^(.*)$ https://my.site.ru$1 permanent;
}

I also have _http://(www.)site.ru http(s)://(www.)othermy.site.ru domains and it's ok.

Starting from version 0.8.4 it should be better to use

        return          301             https://$http_host$request_uri;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM