8

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";
    }
}
2
  • 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
    Commented Aug 28, 2018 at 14:47
  • How do I have to understand, what I have to put in "$1.abcd-efgh" ?
    – rubo77
    Commented Nov 1, 2022 at 14:49

3 Answers 3

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.

3
  • Thanks! That explaind the browser-redirect, and there was a different error in my letsencrypt config :)
    – Richard87
    Commented Sep 17, 2017 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. Commented Sep 17, 2017 at 18:37
  • Yeah, I know, I didn't recognize the hsts flag 😑
    – Richard87
    Commented Sep 17, 2017 at 18:38
8

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

1
  • 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 Commented Sep 27, 2018 at 11:52
7

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.

3
  • Why did you use break; under try_files at /etc/nginx/snippets.d/letsencrypt snippet?
    – raratiru
    Commented Jul 3, 2020 at 9: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
    Commented Jul 3, 2020 at 15:28
  • It seems that it is never evaluated given that try_files redirects to a 404 page as its last resort.
    – raratiru
    Commented Jul 4, 2020 at 21:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .