Nginx configuration leads to endless redirect loop - ssl

So I've looked at every sample configuration I could find and yet every time I try and view a page that requires ssl, I end up in an redirect loop. I'm running nginx/0.8.53 and passenger 3.0.2.
Here's the ssl config
server {
listen 443 default ssl;
server_name <redacted>.com www.<redacted>.com;
root /home/app/<redacted>/public;
passenger_enabled on;
rails_env production;
ssl_certificate /home/app/ssl/<redacted>.com.pem;
ssl_certificate_key /home/app/ssl/<redacted>.key;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X_FORWARDED_PROTO https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Url-Scheme $scheme;
proxy_redirect off;
proxy_max_temp_file_size 0;
location /blog {
rewrite ^/blog(/.*)?$ http://blog.<redacted>.com/$1 permanent;
}
location ~* \.(js|css|jpg|jpeg|gif|png)$ {
if (-f $request_filename) {
expires max;
break;
}
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
Here's the non-ssl config
server {
listen 80;
server_name <redacted>.com www.<redacted>.com;
root /home/app/<redacted>/public;
passenger_enabled on;
rails_env production;
location /blog {
rewrite ^/blog(/.*)?$ http://blog.<redacted>.com/$1 permanent;
}
location ~* \.(js|css|jpg|jpeg|gif|png)$ {
if (-f $request_filename) {
expires max;
break;
}
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
Let me know if there's any additional info I can give to help diagnose the issue.

It's your line here:
listen 443 default ssl;
change it to:
listen 443;
ssl on;
This I'll call the old style.
Also, that along with
proxy_set_header X_FORWARDED_PROTO https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Url-Scheme $scheme;
proxy_redirect off;
proxy_max_temp_file_size 0;
did the trick for me. I see now i am missing the real IP line you have, but so far, this got rid of my infinite loop problem with ssl_requirement and ssl_enforcer.

I've toyed around with a bunch of these answers but nothing worked for me. Then I realized since I use Cloudflare the problem may not be in the server but with Cloudflare. Lo and behold when I set my SSL to Full (Strict) everything works as it should!

I found that it was this line
proxy_set_header Host $http_host;
Which should be changed to
proxy_set_header Host $host;
According to the nginx documentation by using '$http_host you're passing the "unchanged request-header".

Have you tried using "X-Forwarded-Proto" instead of X_FORWARDED_PROTO?
I've run into a problem with this header before, it wasn't causing redirects, but changing this header fixed it for me.

Since you have a rewrite statement found in both ssl and non-ssl sections
location /blog {
rewrite ^/blog(/.*)?$ http://blog.<redacted>.com/$1 permanent;
}
Where is the server section for blog..com?? Could that be the source of the issue?

I had a similar issue for my symfony2 application, albeit form a different cause: I had set fastcgi_param HTTPS off; when I of course needed fastcgi_param HTTPS on; in my nginx configuration.
location ~ ^/(app|app_dev|config)\.php(/|$) {
satisfy any;
allow all;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS on;
}

In case someone else stumbles on this, I was attempting to configure both http and https via the same server {} block, but only added the "listen 443" directive believing that the "this line is default and implied" meant that it would also listen on 80 as well, it didn't. Uncommenting the "listen 80" line so that both listen lines were present corrected the infinite loop. No idea why it would have even been getting a redirect at all, but it did.

For those who are searching desperatly why their owncloud keep making a redirect loop in spite of having a good configuration file, i've found why it's not working.
My config:
nginx + php-fpm + mysql on a fresh centos 6.5
when installing php-fpm and nginx, default permission on /var/lib/php/session/ is root:apache
php-fpm through nginx store php session here, if nginx did not have authorization to write it fail miserably to keep any login session, resulting in an infinite loop.
So juste add nginx in apache group (usermod -a -G apache nginx) or change ownership of this folder.
Have a nice day.

X_FORWARDED_PROTO as in your file can cause errors and it did in my case. X-Forwarded-Proto is correct whereas the hiphens are more important than uppercase or lowercase letters.
You can avoid those problems by sticking to conventions ;)
see also here: Custom HTTP headers : naming conventions and here: http://www.ietf.org/rfc/rfc2047.txt

Related

Nginx Server Block Ordering

I have multiple sites on a subdomain that I am trying to serve using nginx, some of the subdomains are being served with SSL and some are not. I'm having some trouble getting the non-SSL sites to serve properly. Whenever I try to access them, they immediately redirect (with the correct host) to the SSL/HTTPS version. I've attached my location block below. I've read the nginx block on request processing but can't figure out how to force the unencrypted hosts not to get forwarded. (http://nginx.org/en/docs/http/request_processing.html)
server {
listen 80;
server_name dev.example.ca dev.example.server2.example.tl;
location = /favicon.ico { access_log off; log_not_found off; }
location / {
include proxy_params;
proxy_pass http://unix:/home/example/example/socket.sock;
}
location /static {
autoindex on;
alias /home/litobro/example/example/static/;
}
}
server {
listen 80;
server_name dutyroster.example.ca;
location = /favicon.ico { access_log off; log_not_found off; }
location / {
include proxy_params;
proxy_pass http://unix:/home/example2/dutyroster/socket.sock;
}
location /static {
autoindex on;
alias /home/example/example2/static/;
}
location /socket.io {
proxy_pass http://unix:/home/example/example2/socket.sock;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
server {
listen 80;
server_name ex3.server2.example.tl example.ca www.example.ca;
location ~ .well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type text/plain;
}
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.ca www.example.ca example.server2.example.tl;
ssl_certificate /etc/letsencrypt/live/example.ca/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.ca/privkey.pem;
include snippets/ssl-params.conf;
location = /favicon.ico { access_log off; log_not_found off; }
location / {
include proxy_params;
proxy_pass http://unix:/home/example/example3/socket.sock;
}
}
I ordered the server blocks such that the port 80 requests would hopefully get hit first but this still seems to not be working. Thanks in advance for the help! (The server blocks are mostly stripped of the actual domains, though I think I got consistency on the domain replacements)
The cause of the problem is the HSTS header, which is set inside snippets/ssl-params.conf. This header tells the browser that the website will connect only over HTTPS. Here is an example of how this header is set with Nginx:
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
If the header value contains includeSubDomains flag, like in the example above, then the HSTS policy will also apply to all subdomains of the main domain. And that was the reason why your browser tried to send all requests to the subdomain over HTTPS.
Keep in mind that modern browsers store the list of HSTS websites in a special cache, so simply removing or modifying the header in Nginx may not cause any immediate effect. You will need to clear the HSTS cache manually in a manner specific to your browser.
It is also worth mentioning that using the includeSubDomains flag is considered to be a good practice, so keeping it and issuing a certificate for your subdomains instead might be a good idea. Currently there are several certificate authorities, like Let's Encrypt, that provide free of charge and easy to install certificates.
sorry, i don't have many experience with nginx, but you have two server blocks in the end of your conf file. The first listening to port 80 you have "return 301 https://$host$request_uri;" is this line needed?
try commenting that line and see if your non-ssl server names still redirect to https.

nginx and DNS subdomain, too many redirects

I just installed nginx and set it to work over an apache installation. As a matter of fact, my rules are:
server {
listen 80;
server_name example.com;
location / {
proxy_set_header X-Readl-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_pass http://example.com:8080;
}
location ~ /\.ht {
deny all;
}
}
and I wanted to configure a subdomain by:
server {
listen 80;
server_name sub.example.com;
location / {
proxy_pass http://example.com:8080/sub;
}
location ~/\.ht {
deny all;
}
}
And configuring my DNS like so:
NAME | TYPE | TARGET
//empty A 45.23.67.89
sub CNAME example.com
I waited for propagation, but i'm getting "too many redirects" in chrome...
The response is always 301, and redirected to the IP:80, I'm guessing that this is caused because of the A line in DNS... howver domain.com does redirect to apache and I do get the "it works" we all know so well...
Can anyone one point me to the right direction please?
Thanks!
UPDATE:
I added another subdowmain, sub-sub, following EXACTLY the same procedure, but it magically works... need help!
Put
ProxyRequests off
ProxyPass / http://127.0.0.1:2368/
ProxyPassReverse / http:/127.0.0.1:2368/
</VirtualHost>
This config was taken from Apache2 not sure if this will work on Nginx but this fixes it on Apache2. Obviously change the port of the localhost. That config is for Ghost Blog.
This will redirect the visitor to a specific port, this is good if you have HTTPS or SSL enabled and it's getting too many redirect requests. You can set the port to 443 (SSL port).
Ok Solved,
I added headers to the proxy_pass directive and it worked
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
However, it's still adding me a slash (/) at the end of the url, meaning that when i go to sub.example.com i get sub.exmaple.com//

Another nginx reverse proxy issue

I'm putting together an nginx reverse proxy. Here is a working nginx conf file snippet:
upstream my_upstream_server {
server 10.20.30.40:12345;
}
server {
server_name ssl-enabled.example.com;
listen 443 ssl;
ssl_certificate /etc/ssl/server.crt;
ssl_certificate_key /etc/ssl/server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://my_upstream_server/;
proxy_redirect off;
proxy_set_header Host $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-Host $server_name;
}
This allows us to serve requests from my_upstream_server without changing any of its configuration files, and in the bargain serve them up via ssl. So far so good.
What I really want to do, though, is configure this so that instead of going to https://ssl-enabled.example.com/, we can direct users to https://ssl-enabled.example.com/upstream/. (I want to do this so we can have multiple virtual hosts running, each proxying a different service that we want to ssl-enable.) I've tried changing the location line from location / to location /upstream/; when I do that, the index page of the application (https://ssl-enabled.example.com/upstream/) renders fine, but pages underneath it generate 404 errors. Here's an example:
This link is broken
Nginx tries to serve /some/link.html instead of /upstream/some/link.html, which doesn't work.
I tried to create a rewrite that would send the request to /upstream$1, but for the main page (which nginx now thinks is https://.../upstream/) it goes into an endless loop, tries to serve /upstream/upstream/upstream/..., and of course fails.
I suspect I'm missing something both vital and simple, but so far I haven't figured out what it might be. The documentation may provide a clue, but if it does I'm not seeing it. Any help from the nginx experts out there would be greatly appreciated. Thanks.
The config below should do a similar redirect as you mentioned without entering a loop:
upstream my_upstream_server {
server 10.20.30.40:12345;
}
server {
server_name ssl-enabled.example.com;
listen 443 ssl;
ssl_certificate /etc/ssl/server.crt;
ssl_certificate_key /etc/ssl/server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location /upstream {
proxy_pass http://my_upstream_server/;
proxy_redirect off;
proxy_set_header Host $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-Host $server_name;
}
location / {
return 301 https://ssl-enabled.example.com/upstream$request_uri;
}
}
Basically two location blocks.
One for requests starting with "upstream", which are served, and the other for those without, which are redirected.
Alexey is right about / being easier to use, and about the time he posted his comment, I came to the realization that since I can create DNS entries for example.com, instead of trying to direct people to https://server.example.com/upstream/ it would be much easier to just create a DNS entry for https://upstream.example.com/
So that's what I did and it looks like the code is doing exactly what I want. Thanks to Alexey and Dayo for their replies.

How do I force Ghost's admin page to be server over SSL when ghost is installed in a subdirectory?

I am using Ghost as a blogging platform and nginx as a reverse proxy for ghost as detailed in the documentation. Ghost is installed in a subdirectory and is served over the domain http://example.com/blog whereas the static website is served over example.com
I have set up SSL on my server and want to serve the ghost login page (example.com/blog/ghost) over SSL while serving the rest of the pages over normal HTTP. However if I use forceAdminSSL:true and try to go to http://example.com/blog/ghost it should automatically redirect me to https://example.com/blog/ghost. Instead I'm redirected to https://example.com/ghost and end up with 404 error. The only work around I have found that works is to use foreAdminSSL:{redirect:false} which is clumsy because then I have to manually type https in the address bar instead of http.
How do I server Ghost Admin panel over ssl while ghost is installed in a subdirectory? I guess this has something to do with configuration in nginx.
My nginx config block
server {
listen 80;
listen 443 ;
server_name *.example.com;
server_name example.com;
ssl on;
ssl_certificate /etc/nginx/ssl/certificate.crt;
ssl_certificate_key /etc/nginx/ssl/key.key;
location ^~/blog {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:2786;
proxy_redirect off;
}
location / {
root "/home/ubuntu/somedirectory/";
index index.html;
}
I think you haven't entered the config URL while setting up Ghost correctly.
You can do this by running the following commands:
ghost config URL https://my-domain.com/blog/
ghost restart
If this doesn't solve the problem, you can check out a detailed tutorial, solving this issue, on my blog here

HHVM serve multiple domain

I'm try to host several domain on the same VPS, using HHVM to serve the pages.
I'm wondering how can I write the VirtualHost in order to point the right folder in my /var/www directory ?
For example xxx.domain.com >> /var/www/domain.com/
Good news. Since the release of HHVM 2.3 (Dec 13, 2013), you can run HHVM in FCGI mode. Use either Nginx or Apache and it works wonderfully.
Reference: http://www.hhvm.com/blog/1817/fastercgi-with-hhvm
With an older version of HHVM you can run multiple server instances on internal ports, i.e. 8001, 8002, etc. Then configure Nginx as a reverse proxy. (Apache can do that too).
upstream node1{
server 127.0.0.1:8001;
}
upstream node2{
server 127.0.0.1:8002;
}
server {
...
server_name server1.com;
location ~ \.php$ {
proxy_pass http://node1;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $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-Ssl on; #only for https
}
}
server {
...
server_name server2.com;
location ~ \.php$ {
proxy_pass http://node2;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_redirect off;
proxy_buffering off;
proxy_set_header Host $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-Ssl on; #only for https
}
}
Of course this setup takes up a lot of memory. Go with 2.3 if you can upgrade.
Apparently is not yet possible. Accordling to the official github repository where the code is hosted exists an open issue about the same issue you are asking and it's tag for wishlist / feature request.
Probably the best way to solve this is to run a HHVM server for each domain (mean each domain you need a different root folder) and use Apache or Nginx as proxy.
On Nginx, the only way I was able to get this to work was to use / as SourceRoot for HHVM, and to add a / in fastcgi_param SCRIPT_FILENAME /$document_root$fastcgi_script_name; in my /etc/nginx/hhvm.conf file. With that combination, I'm running ~7 sites without a problem so far. I'm running Ubuntu 13.10 64-bit.
In /etc/hhvm/server.hdf, change SourceRoot = /var/www to SourceRoot = /:
Server {
Port = 9000
SourceRoot = /
DefaultDocument = index.php
}
In /etc/nginx/hhvm.conf, add a / in front of $document_root$fastcgi_script_name;:
location ~ \.php$ {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
fastcgi_keep_conn on;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /$document_root$fastcgi_script_name;
fastcgi_intercept_errors on;
fastcgi_read_timeout 300;
include fastcgi_params;
}
You may also need to change fastcgi_param SCRIPT_FILENAME $fastcgi_script_name; to fastcgi_param SCRIPT_FILENAME /$document_root$fastcgi_script_name;, at least I had to with mine.
There may be security implications by using / as your SourceRoot - I mitigate this as much as I can by firewalling port 9000 so only localhost can reach it. Or you can use a socket instead. Not fool-proof, but from what I've seen so far it's OK.