Have a setup like the following:

haproxy listening on 443 -> nginx listening on 4545

I have also tried this with:

sslh listening on 443 -> nginx listening on 4545

This is on ubuntu server

For some reason, nginx fails if the url does not have a trailing slash. So, http://example.com/mydir/ works, and http://example.com/mydir fails

For example:

curl -v http://example.com/mydir


<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.10.3 (Ubuntu)</center>


curl -v http://example.com/mydir/

gives the actual response (the website).

The haproxy config is:

tune.ssl.default-dh-param 2048
log /dev/log    local0
log /dev/log    local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy

# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# I removed the default ciphers from this code block as it was not
# coming out right in stackexchange. 

log global
option  httplog
option  dontlognull
    timeout connect 5000
    timeout client  50000
    timeout server  50000

frontend ssl
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if HTTP
# use_backend ssh if { payload(0,7) -m bin 5353482d322e30 }
use_backend main-ssl if { req.ssl_hello_type 1 }
default_backend openvpn

backend main-ssl
mode tcp
server main-ssl

backend openvpn
mode tcp
timeout server 2h
server openvpn-localhost

Relevant nginx config is:

server {
listen ssl;
# Redirect non-https traffic to https
if ($scheme != "https") {
    return 301 https://$host$request_uri;
# if a visitor goes to root, and only root, 
# we redirect 
location / {
    rewrite ^(.*)$ http://example.net permanent;
# I have several definitions like this, and only this:
location /mydir {
alias /home/abcdefg/public_html;
location ~ [^/]\.php(/|$) {
    if (!-f $request_filename) {
        return 404;
include /etc/nginx/fastcgi_params;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;

I have read 452 FAQs and several posts that seem to pertain to the same issue, but in nearly all of them, they are redirecting without haproxy, or they simply have a config error (like they are using a base URL with a trailing slash in the config).

This person seems to have had the exact same issue, but it was that the haproxy was not compiled with ssl support. haproxy.org

haproxy -vv output:

HA-Proxy version 1.6.3 2015/12/25
Copyright 2000-2015 Willy Tarreau <willy@haproxy.org>

Build options :
TARGET  = linux2628
CPU     = generic
CC      = gcc
CFLAGS  = -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2

Default settings :
maxconn = 2000, bufsize = 16384, maxrewrite = 1024, maxpollevents = 200

Encrypted password support via crypt(3): yes
Built with zlib version : 1.2.8
Compression algorithms supported : identity("identity"), deflate("deflate"),     raw-deflate("deflate"), gzip("gzip")
Built with OpenSSL version : OpenSSL 1.0.2g  1 Mar 2016
Running on OpenSSL version : OpenSSL 1.0.2g  1 Mar 2016
OpenSSL library supports TLS extensions : yes
OpenSSL library supports SNI : yes
OpenSSL library supports prefer-server-ciphers : yes
Built with PCRE version : 8.38 2015-11-23
PCRE library supports JIT : no (USE_PCRE_JIT not set)
Built with Lua version : Lua 5.3.1
Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT    IP_FREEBIND

Available polling systems :
  epoll : pref=300,  test result OK
   poll : pref=200,  test result OK
 select : pref=150,  test result OK
Total: 3 (3 usable), will use epoll.

I have tried adding a / to the location and alias to no avail. I have also tried modifying the redirect (although I should not be hitting the redirect). Finally, I am doing curl with and without https with the same result, which leaves me a little baffled at the actual problem. Any input would be appreciated.

The output of curl -vv https://example.com/dir is as follows (sanitized to be example.com with an ip of

*   Trying
* Connected to example.com ( port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: /etc/ssl/certs
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=example.com
*  start date: May  7 07:12:44 2019 GMT
*  expire date: Aug  5 07:12:44 2019 GMT
*  subjectAltName: host "example.com" matched cert's "example.com"
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
> GET /dir HTTP/1.1
> Host: example.com
> User-Agent: curl/7.58.0
> Accept: */*
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.10.3 (Ubuntu)
< Date: Fri, 31 May 2019 21:07:56 GMT
< Content-Type: text/html
< Content-Length: 194
< Location: https://example.com:4545/dir/
< Connection: keep-alive
< X-Frame-Options: SAMEORIGIN
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Strict-Transport-Security: max-age=15768000
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.10.3 (Ubuntu)</center>
* Connection #0 to host example.com left intact

Yet another note, while doing the curl tests, if I look at the nginx logs I see the following if I do curl -v example.com/dir - - [31/May/2019:18:33:34 -0500] "GET /dir HTTP/1.1" 301 194 "-" "curl/7.58.0"

And this is what I see when I do curl -v example.com/dir/ - - [31/May/2019:18:33:30 -0500] "GET /dir/ HTTP/1.1" 200 22839 "-" "curl/7.58.0"

  • This isn't failing -- it's working as expected with curl. If you use the -v option, you should see why you get the response that you get -- there's a Location header, which curl ignores by default, but normal web browsers don't ignore. You will need to use curl --location or curl -L if you want curl to work like a web browser. (I'm not sure why this isn't default curl behavior, but it isn't). – Michael - sqlbot May 31 at 19:56
  • Curl or not, in a browser the behaviour is exactly the same. example.com/dir fails, it sends me to example.com:4545/dir which fails. example.com/dir/ succeeds. I will also update this question in that I switched from haproxy to sslh and I get the same results, so I think this is totally nginx configuration and not haproxy. – number9 May 31 at 20:33
  • That's unexpected. Please show the output from curl -v http://example.com/mydir, particularly the Location response header. That should be the key to all of this. – Michael - sqlbot May 31 at 20:38
  • When I execute curl -v example.com/dir the location comes back as: example.com:4545/dir Note, it FAILS, but the location is /dir/. When I execute curl -v example.com/dir there is no location flag in the output, it goes from Content-length to Last modified. I even saved it and grepped, as I thought I was going nuts. Huh. – number9 May 31 at 20:46
  • 1
    Okay, I see it, now. Location: https://example.com:4545/dir/. The 301 redirect is correct, because directories must be requested with the canonical path (with a trailing slash). The inclusion of the port in the redirect is incorrect, and it's not inmediately apparent why nginx is including that in the redirect. – Michael - sqlbot May 31 at 23:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.