0

I'm trying to avoid redirect chains in Nginx, which I am using as a reverse proxy to Apache.

I've been able to find plenty of documentation about redirecting HTTP to HTTPS.

I've also found plenty of documentation about redirecting www to naked domain (or vice versa).

The vhosts/websites are (for the most part) subdomains of my primary website, example.com.

Background: I am using this configuration because I am most familiar with Apache but I appreciate the performance improvements of Nginx and, more importantly, I have a NodeJS application running on the server and I'd like users to be able to access it without appending a port to the request. Nginx listens on port 80 for HTTP and port 443 for HTTPS and routes requests to the ports that I have configured Apache and NodeJS to use (port 8080 for Apache and port 4000 for Node).

Edit: I found the config generator tools at nginxconfig.io to be very helpful and much of what I got from there has been included unchanged.

For the "main" website, I'm using the www subdomain so I need to redirect the following domains to https://www.example.com:

http://example.com
http://www.example.com
https://example.com

Everything I can find seems to handle this in 2 redirects such as:

http://example.com to https://example.com to https://www.example.com

The answer in this question (which is actually a redirect of www to the naked domain), uses an if statement but Nginx docs suggest avoiding if statements for performance reasons.

Question 1 Is there are way to handle these redirects in one redirect, and without triggering warnings about having blocks with duplicate server names?

Question 2 When I set up the block for port 80, what parameters do I need to include if all I am doing is redirecting?

My .conf file looks like this:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name www.example.com;
    root /var/www/html/example.com;

    # SSL
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    # security
    include nginxconfig.io/security.conf;

    # logging
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log warn;

    # index.html fallback
    location / {
        try_files $uri $uri/ /index.html;
    }

    # additional config
    include nginxconfig.io/general.conf;
}

# HTTP redirect
server {
    listen 80;
    listen [::]:80;

    server_name example.com;

    include nginxconfig.io/letsencrypt.conf;

    location / {
        return 301 https://$server_name$request_uri;

    }
}

Do I need to have include nginxconfig.io/letsencrypt.conf; in the block for port 80?

Do I need to add http2 after the listen directives in the block for port 80 so that HTTP2 requests over HTTP get redirected to HTTPS while still supporting HTTP2 or is it sufficient to just include the http2 directive in the block for HTTPS/port 443.

The contents of nginxconfig.io/letsencrypt.conf are:

# ACME-challenge
location ^~ /.well-known/acme-challenge/ {
    root /var/www/_letsencrypt;
}

Contents of .../security.conf are:

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
#add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
#add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

# . files
location ~ /\.(?!well-known) {
    deny all;
}
location ~* (?:#.*#|\.(?:bak|conf|dist|fla|in[ci]|log|orig|psd|sh|sql|sw[op])|~)$ {
  deny all;
}

I do plan to implement HSTS eventually, but obviously, need to get my basic configuration sorted out first.

Contents of .../general.conf:

# favicon.ico
location = /favicon.ico {
    log_not_found off;
    access_log off;
}

# robots.txt
location = /robots.txt {
    log_not_found off;
    access_log off;
}

# gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
adam-asdf
  • 191
  • 1
  • 11

1 Answers1

1

The preferred way is to have a separate redirect from http://example.com to https://example.com which then redirects to https://www.example.com.

This is because how HSTS headers are used. Further information is at https://www.danielmorell.com/blog/how-to-configure-hsts-on-www-and-other-subdomains

To accommodate this, you need to replace:

server_name example.com;

with:

server_name example.com www.example.com;

in your HTTP server block.

I assume that letsencrypt.conf contains only TLS related settings, and those are not needed in the HTTP server block. However, the settings need to be applies to the HTTPS server block.

In addition, you need the 301 redirect for your example.com domain:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name example.com;

    # SSL
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    include nginxconfig.io/letsencrypt.conf;

    return 301 https://www.example.com;
}
Tero Kilkanen
  • 36,796
  • 3
  • 41
  • 63