I have a clustered infrastructure. I use nginx for SSL termination in front of varnish. The backends of varnish are apache web servers.and I also have a haproxy as a load balancer which sends HTTPS requests directly to nginx and sends HTTP requests directly to varnish servers. The problem is that when I start nginx everything is OK for some while, but after that I get the too_many_error_redirects in browsers when browsing ssl websites!! I think there is something wrong with my configurations, but I don't know which configurations (nginx or varnish) are the cause of this error. When I forward requests directly to the webservers everything is OK, so may be the varnish config has problem. here are my configurations:
Nginx config: domain_name.conf
server {
listen 443;
server_name mydomain.com;
ssl on;
ssl_certificate /etc/nginx/ssl/domain_name_bundle.pem;
ssl_certificate_key /etc/nginx/ssl/my_key.key;
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 10m;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS +RC4 RC4";
add_header Strict-Transport-Security "max-age=31536000";
server_tokens off;
proxy_pass_header Server;
location / {
proxy_pass http://cache-servers;
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 https;
proxy_set_header X-Forwarded-Port 443;
proxy_redirect off;
proxy_set_header Host $host;
}
}
upstream cache-servers
{
ip_hash;
#cache servers
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
Varnish config:
vcl 4.0;
import directors;
# Check backend health
probe backend_healthcheck {
.url = "/";
.timeout = 10s;
.window = 5;
.threshold = 3;
.interval = 5s;
.expected_response = 200;
}
backend web1 {
.host = "192.168.1.105";
.port = "8080";
.probe = backend_healthcheck;
}
backend web2 {
.host = "192.168.1.106";
.port = "8080";
.probe = backend_healthcheck;
}
sub vcl_init {
new apache = directors.round_robin();
apache.add_backend(web1);
apache.add_backend(web2);
}
sub vcl_recv {
set req.backend_hint = apache.backend();
set req.http.X-Forwarded-For = client.ip;
if (req.method == "GET" && (req.url ~ "^/?mylogout=")) {
unset req.http.Cookie;
return (pass);
}
#we should not cache any page for Prestashop backend
if (req.method == "GET" && (req.url ~ "^/admin70")) {
return (pass);
}
#we should not cache any page for customers
if (req.method == "GET" && (req.url ~ "^/authentification" || req.url ~ "^/my-account")) {
return (pass);
}
#we should not cache any page for customers
if (req.method == "GET" && (req.url ~ "^/identity" || req.url ~ "^/my-account.php")) {
return (pass);
}
#we should not cache any page for sales
if (req.method == "GET" && (req.url ~ "^/cart.php" || req.url ~ "^/order.php")) {
return (pass);
}
#we should not cache any page for sales
if (req.method == "GET" && (req.url ~ "^/addresses.php" || req.url ~ "^/order-detail.php")) {
return (pass);
}
#we should not cache any page for sales
if (req.method == "GET" && (req.url ~ "^/order-confirmation.php" || req.url ~ "^/order-return.php")) {
return (pass);
}
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
#pass feeds
if (req.url ~ "/feed")
{
return (pass);
}
if (req.url ~ "/wp-(login|admin)" || (req.method == "GET" && req.url ~ "^/admin") || (req.method == "GET" && req.url ~ "^/user"))
{
#unset req.http.cookie;
return (pass);
}
#cache everything left behind
return(hash);
}
sub vcl_backend_response {
if (!(bereq.url ~ "(wp-(login|admin)|admin)")) {
unset beresp.http.set-cookie;
set beresp.ttl = 10m;
}
set beresp.grace = 2h;
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
if (obj.hits > 0) {
set resp.http.X-Cache-Lookup = "HIT";
} else {
set resp.http.X-Cache-Lookup = "MISS";
}
unset resp.http.X-Varnish;
unset resp.http.Via;
unset resp.http.Server;
unset resp.http.X-Powered-By;
#return (deliver);
}
Best Answer
The problem here is very common: you're doing redirect http -> https at application level.
Apache/PHP don't know they are already running in SSL, as apache is running in http (then passing to varnish, then passing to nginx).
The solution is simple: your PHP Application needs to have the PHP ENV variable
$_SERVER['HTTPS'] = "on"
.You can do in different ways:
Plus: I would also do a redirect from http to https at varnish level: add a custom header like
X-Nginx = on
when request come from your nginx. In varnish read that header, if it doesn't exist, then redirect the user to https URL.NOTE: If you're using wordpress (as it seems in your vcl file) don't forget to add this: