I've spent the last few hours trying to understand what is happening with the nginx location documentation and questions by other people on serverfault (like this one). From everything I gathered, my solution should work, but it just doesn't.
So as the title says, I'd like to configure nginx such that it will serve the challange file that acme.sh creates, but redirect everything else to a specific https domain of mine configured in the same server (this one works perfectly fine).
My server block:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
location ^~ /.well-known/acme-challenge/ {
try_files $uri /;
}
location / {
return 301 https://example.com$request_uri;
}
}
For testing purposes, I've created a file myself in this directory: /usr/share/nginx/html/.well-known/acme-challange/somefile
.
The testing curl command: curl http://example.com/.well-known/acme-challange/somefile
Problem is: No matter which request, everything is always redirected. The moment I remove the /
location block, however, the curl command will give me the contents of my test file. Obviously no redirects will be done in that case.
From the nginx documentation, my understanding is with the modifier I put, once the acme prefix matches (which it obviously does since it works when it is the only location block), it should not try to match anything else.
I've also tried simply removing the try_files
directive, but it makes no difference. Instead of return 301 ...
I've also tried to do a rewrite
, which also has the exact same behavior: There will always be a redirect no matter which URI is used.
For some reason the /
location block is always used once it's there, even if there are more specific locations that match and even if said locations have the ^~
modifier.
Another variant I just tried:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
location /.well-known/acme-challenge/ {
try_files $uri =404;
}
return 301 https://example.com$request_uri;
}
Very similar behavior: If the return is present -> redirect. If I comment it out: my test file is served to me with the curl call. One curious thing is that I cannot move the root
directive inside the location blocks. If I do, nginx starts to look for files in /etc/nginx/html
, which is apparently the default root, and will throw an error in the logs ("no such file or directory") when I do the http request.
Btw, I did not remove anything from this server block, I just changed the domain name to example.com. It is also the only server block listening on port 80 and no other configs are included in the file. Of course, after every change, I also restarted the nginx service. If it matters, my nginx version is 1.18.0
Output of nginx -T
as requested in comments (domain name was changed and mime.types file was cut due to character limits here):
2021/04/23 02:21:00 [warn] 88558#88558: could not build optimal types_hash, you should increase either types_hash_max_size: 1024 or types_hash_bucket_size: 64; ignoring types_hash_bucket_size
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# configuration file /etc/nginx/nginx.conf:
user html;
worker_processes auto;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES128+EECDH:AES256+EECDH:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256";
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ecdh_curve secp384r1:prime256v1;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
gzip on;
# for certificate verification
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
location /.well-known/acme-challenge/ {
try_files $uri =404;
}
#return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_certificate ssl/example.com.crt;
ssl_certificate_key ssl/example.com.key;
#access_log logs/host.access.log main;
root /usr/share/nginx/html;
index index.html index.php;
location / {
fastcgi_param SCRIPT_FILENAME $document_root/src/index.php;
include fastcgi_params;
# override SCRIPT_NAME which was set in fastcgi_params
fastcgi_param SCRIPT_NAME /src/index.php;
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
}
location /public/ {
allow all;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
#error_page 500 502 503 504 /50x.html;
#location = /50x.html {
# root /usr/share/nginx/html;
#}
}
}
# configuration file /etc/nginx/mime.types:
types {
#REMOVED BECAUSE OF CHARACTER LIMIT
}
# configuration file /etc/nginx/fastcgi_params:
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;