I have a Hunchentoot application running on http://localhost:4242
. This application uses a non-SSL acceptor (i.e. hunchentoot:easy-acceptor
instead of hunchentoot:easy-ssl-acceptor
). I set up Nginx as a reverse proxy to serve this application using SSL. This is the Nginx configuration:
server {
listen 443 ssl;
server_name example.test;
ssl_certificate /etc/my-ssl/certs/example.pem;
ssl_certificate_key /etc/my-ssl/private/example.key;
location / {
include /etc/nginx/proxy_params;
proxy_pass http://localhost:4242;
}
}
where /etc/nginx/proxy_params
is
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
The problem is that Hunchentoot does not know that the reverse proxy (i.e. Nginx) is actually serving the client using HTTPS. As a result, (hunchentoot:redirect "/some-page/")
will redirect to http://example.test/some-page/
instead of https://example.test/some-page/
. Hunchentoot does not take into account the X-Forwarded-Proto
header.
To solve this problem, I have written an alternative redirect function which I use instead of #'hunchentoot:redirect
:
(defun my-redirect (target
&rest args
&key (protocol
(if (string= (hunchentoot:header-in* :x-forwarded-proto)
"https")
:https
:http))
&allow-other-keys)
(apply #'hunchentoot:redirect target :protocol protocol args))
Is this a correct method of solving the problem? Are there better ways?
(Hunchentoot 1.3.0; SBCL 2.2.2; Nginx 1.18.0; Ubuntu 20.04)
EDIT: In Hunchentoot's issue tracker: Incorrect redirection to HTTP when application is served using HTTPS reverse proxy
An option is to subclass hunchentoot:easy-acceptor
and specialize the acceptor-ssl-p
method for your acceptor. This would enable you to call hunchentoot:redirect
and have the protocol decision happen behind the scenes.
(defclass my-easy-acceptor (hunchentoot:easy-acceptor)
())
(defmethod hunchentoot:acceptor-ssl-p ((acceptor my-easy-acceptor))
(string= (hunchentoot:header-in* :x-forwarded-proto) "https"))
However, it seems to me (from the nginx configuration) that you are always serving HTTPS - so you could omit the logic and always return t
.