7

I have a simple config, redirect everything except letsencrypt requests to https, and then have my virtual hosts only on https..

Currently all my requests are redirected to https, and then a 404 for letsencrypt:

This is my config...

server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name _;
        location ^~ /.well-known/acme-challenge/ {
            allow all;
            default_type text/plain;
            return 200 "$1.abcd-efgh";
        }

        location / {
            return 301 https://$host$request_uri;
        }
}

server {
    listen 443 ssl;
    server_name plex.my_domain.com;

    ssl_session_timeout 30m;
    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
    ssl_certificate      /root/.acme.sh/plex.my_domain.com/fullchain.cer;
    ssl_certificate_key  /root/.acme.sh/plex.my_domain.com/plex.my_domain.com.key;
    ssl_session_cache shared:SSL:10m;


    add_header X-Xss-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Strict-Transport-Security "max-age=2592000; includeSubdomains" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    proxy_hide_header X-Powered-By;
    add_header 'Referrer-Policy' 'no-referrer';
    add_header Content-Security-Policy "frame-ancestors my_domain.com plex.my_domain.com;";


    location / {
        proxy_pass http://127.0.0.1:32400;

        proxy_set_header Range $http_range;
        proxy_set_header If-Range $http_if_range;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        #Next three lines allow websockets
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
Richard87
  • 240
  • 1
  • 2
  • 6
  • 1
    You don‘t actually need to have the letsencrypt-location in the http-block. My config has always been to only return 301 in the http-block and have the nginx-location in the https-block. This works fine for renewals as well as anything else. You might, how ever, encounter problems trying to renew expired certs. But your certs should always be renewed prior to expiration anyways. – cetteup Aug 28 '18 at 14:47
  • How do I have to understand, what I have to put in `"$1.abcd-efgh"` ? – rubo77 Nov 01 '22 at 14:49

3 Answers3

6

You can create a snippet to use in your config in /etc/nginx/snippets.d/letsencrypt

location ~ /\.well-known/acme-challenge/ {
  allow all;
  default_type "text/plain";
  root /usr/share/nginx/html;
  try_files $uri =404;
  break;
} 

then include it in your location:

server {
  listen 80;
  server_name *.yourdomain.org yourdomain.org;
  location / {
    return 301 https://$host$request_uri;
  }
}

server {
  listen 443 ssl;
  server_name *.yourdomain.org yourdomain.org;
  include snippets.d/letsencrypt;
  ssl_certificate     /etc/letsencrypt/live/yourdomain.org/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/yourdomain.org/privkey.pem;
  location / {
      # your config for the domain goes here
  }
}
  1. Make sure, the 301 redirect is inside a location block (otherwise it will be called everytime).

  2. Make sure, the redirect leads to a whole domain and not a subdirectory of another domain (like https://yourdomain.org/subdir$request_uri) so the well-known challenge is routed correctly.

rubo77
  • 2,469
  • 4
  • 34
  • 66
  • Why did you use [`break;`](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#break) under `try_files` at `/etc/nginx/snippets.d/letsencrypt ` snippet? – raratiru Jul 03 '20 at 09:20
  • 1
    `break` completes processing of current rewrite directives and non-rewrite processing continues within the current location block only. ... I am not sure if it is really needed – rubo77 Jul 03 '20 at 15:28
  • It seems that it is never evaluated given that `try_files` redirects to a 404 page as its last resort. – raratiru Jul 04 '20 at 21:28
6

Well, wasn't the HSTS in my case

server {
    # ...
    location /.well-known/acme-challenge/ {
        # put your configuration here, if needed
    }
    location / {
        return 301 https://$server_name$request_uri;
    }
}

Summary: put the 301 redirect inside the / location

ᴍᴇʜᴏᴠ
  • 577
  • 1
  • 6
  • 20
  • 2
    Explanation: from what I understood, Nginx looks for the most specific `location` directive, if you don't put your redirect into a `location`, it will always get executed – ᴍᴇʜᴏᴠ Sep 27 '18 at 11:52
4

You have HSTS headers set in your https server block. This means that if you visit your site with https once with your browser, your browser will always connect to your domain with https after the first visit.

This means that you cannot test your configuration with a browser. You need to test it with curl or similar tool that doesn't store HSTS lists.

There is a minor tweak for your configuration, you can use a simple prefix match location /.well-known/acme-challenge for your LetsEncrypt location. nginx will use the most specific match from the location blocks.

Tero Kilkanen
  • 36,796
  • 3
  • 41
  • 63
  • Thanks! That explaind the browser-redirect, and there was a different error in my letsencrypt config :) – Richard87 Sep 17 '17 at 13:05
  • Actually there isn't a redirect going on at all, it is just that the browser automatically loads the https version of the page. Although the end result looks like a redirect, it is a different mechanism. – Tero Kilkanen Sep 17 '17 at 18:37
  • Yeah, I know, I didn't recognize the hsts flag – Richard87 Sep 17 '17 at 18:38