Nginx is configured for SSL, configuration? - ssl

I have Nginx configured with SSL. It requests certificates as users access the site, this is working fine. If I wanted a particular page of my site to be accessible without requiring the certificate, how can that be accomplished? An important factor to me is keeping all other pages requiring the certificate, and only one page as not requiring the certificate. Any help is appreciated.

This is not easy with nginx. You can set ssl_verify_client optional;, but then you will need to check the $ssl_client_verify variable manually.
See this document for more.

Nginx finds the longest matching location so you can specify your exception(s) and then pass all other traffic to https, like so:
server {
listen 443;
location /exception/ { # redirect https requests to http server
return 301 http://$server_name$request_uri;
}
# ...
}
server {
listen 80;
location / { # the default location redirects to https
return 301 https://$server_name$request_uri;
}
location /exception/ {} # do not redirect requests
# ...
}
I hope this is helpful.

Related

How do I fix an infinite redirect loop on a self-hosted nginx server?

I'm learning how to build and host my own website using Python and Flask, but I'm unable to make my website work as I keep getting an infinite redirect loop when I try to access my website through my domain name.
I've made my website using Python, Flask, and Flask-Flatpages. I uploaded the code to GitHub and pulled it onto a Raspberry Pi 4 that I have at my house. I installed gunicorn on the RasPi to serve the website and set up two workers to listen for requests. I've also set up nginx to act as a reverse proxy and listen to requests from outside. Here is my nginx configuration:
server {
if ($host = <redacted>.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
# listen on port 80 (http)
listen 80;
server_name <redacted>.com www.<redacted>.com;
location ~ /.well-known {
root /home/pi/<redacted>.com/certs;
}
location / {
# redirect any requests to the same URL but on https
return 301 https://$host$request_uri;
}
}
server {
# listen on port 443 (https)
listen 443;
ssl on;
server_name <redacted>.com www.<redacted>.com;
# location of the SSL certificate
ssl_certificate /etc/letsencrypt/live/<redacted>.com/fullchain.pem; # m$
ssl_certificate_key /etc/letsencrypt/live/<redacted>.com/privkey.pem; #$
# write access and error logs to /var/log
access_log /var/log/blog_access.log;
error_log /var/log/blog_error.log;
location / {
# forward application requests to the gunicorn server
proxy_pass http://localhost:8000;
proxy_redirect off;
proxy_set_header X_Forwarded_Proto $scheme;
proxy_set_header Host $host;
location /static {
# handle static files directly, without forwarding to the application
alias /home/pi/<redacted>.com/blog/static;
expires 30d;
}
}
When I access the website by typing in the local IP of the RasPi (I've set up a static IP address in /etc/dhcpcd.conf), the website is served just fine, although it seems like my browser won't recognize the SSL certificate even though Chrome says the certificate is valid when I click on Not Secure > Certificate next to the .
To make the website public, I've forwarded port 80 on my router to the RasPi and set up ufw to allow requests only from ports 80, 443, and 22. I purchased a domain name using GoDaddy, then added the domain to CloudFlare by changing the nameservers in GoDaddy (I'm planning to set up cloudflare-ddns later, which is why I added the domain to CloudFlare in the first place). As a temporary solution, I've added the current IP of my router to the A Record in the CloudFlare DNS settings, which I'm hoping will be the same for the next few days.
My problem arises when I try to access my website via my public domain name. When I do so, I get ERR_TOO_MANY_REDIRECTS, and I suspect this is due to some problem with my nginx configuration. I've already read this post and tried changing my CloudFlare SSL/TLS setting from Flexible to Full (strict). However, this leads to a different problem, where I get a CloudFlare error 522: connection timed out. None of the solutions in the CloudFlare help page seem to apply to my situation, as I've confirmed that:
I haven't blocked any CloudFlare IPs in ufw
The server isn't overloaded (I'm the only one accessing it right now)
Keepalive is enabled (I haven't changed anything from the default, although I'm unsure whether it is enabled by default)
The IP address in the A Record of the DNS Table matches the Public IP of my router (found through searching "What is my IP" on google)
Apologies if there is a lot in here for a single question, but any help would be appreciated!
I only see one obvious problem with your config, which is that this block that was automatically added by certbot should probably be removed:
if ($host = <redacted>.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
Because that behavior is already specified in the location / {} block, and I think the Certbot rule may take effect before the location ~ /.well-known block and break that functionality. I'm not certain about that, and I don't think that would cause the redirects, but you can test the well-known functionality yourself by trying to access http://yourhost.com/.well-known and seeing if it redirects to HTTPS or not.
On that note, the immediate answer to your question is, get more information about what's happening! My next step would be to see what the redirect loop is - your browser may show this in its network requests log, or you can use a command-line tool like curl or httpie or similar to try to access your site via the hostname and see what requests are being made. Is it simply trying to access the same URL over and over, or is it looping through multiple URLs? What are they? What does that point at?
And as a side note, it makes sense that Chrome wouldn't like your certificate when accessing it via IP - certificates are tied to one or more hostnames, so when you're accessing it over an IP address, the hostname doesn't match, so Chrome is probably (correctly) pointing that out and warning you that you're not at the hostname the certificate says you should be at.

Browser doesn't send the cookie from domain with www after redirect

I have a staging version for my website which is beta.example.com. I recently added cookies authentication with the following setting:
response.cookie(tokenName, token, {
httpOnly: true,
expires: new Date(Date.now() + (tokenExpirationSec * 1000))
})
In staging authentication works.
When I deployed the code to production the cookie was successfully set (via Set-Cookie header) but was not sent to the server in server-side requests. So when I would refresh the logged in state would disappear but would be preserved in client-side requests.
It is worth noting that there's a 301 redirect from example.com to www.example.com. Also the host header was www.example.com in production.
I eventually solved the issue by adding domain parameter when setting the cookie as follows:
response.cookie(tokenName, token, {
httpOnly: true,
expires: new Date(Date.now() + (tokenExpirationSec * 1000)),
domain: '.example.com'
})
However I don't completely understand the source of the problem. According to MDN
Domain specifies allowed hosts to receive the cookie. If unspecified, it defaults to the host of the current document location, excluding subdomains. If Domain is specified, then subdomains are always included.
So when I was in staging with beta.example.com without explicitly setting domain the implicit domain would be example.com according to MDN and beta.example.com would be excluded. But authentication did work in staging!
But I have the same situation in production with www.example.com so why doesn't it work in production?
This is the nginx config which does the redirect:
server {
listen 80 default_server;
server_name beta.example.com;
location / {
include proxy_pass.inc;
}
}
server {
listen 80 default_server;
server_name www.example.com;
location / {
include proxy_pass.inc;
}
}
server {
listen 80;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
I believe you are misinterpreting the MDN documentation.
If unspecified, it defaults to the host of the current document location, excluding subdomains.
Meaning if you are on beta.example.com then Domain will be set to the value beta.example.com. This will exclude the cookie from other subdomains.
You must explicitly set Domain if you would like to use the cookie on all subdomains.

All my sites are return 301 instead of 200

All my sites are return 301 response code instead of 200. My sites are hosted on amazon aws and my dns are managed by cloudflare. I am using free ssl from cloudflares.
I was testing my site in third party websites. And it seems everywhere i am getting the following error:
Your server did not return a valid HTTP code (You returned 301 when you should have returned 200).
My server is configured using nginx. this is my nginx server code:
server {
listen 80;
listen 443 ssl;
root /media/6sense/www/shajao.com;
index index.html index.htm index.nginx-debian.html;
server_name shajao.com www.shajao.com;
location / {
try_files $uri$args $uri$args/ /index.html;
}
}
This is my website.
https://shajao.com/
Your site returns a 301 when requested over HTTP, in order to redirect to HTTPS. That is, if someone tries to fetch http://shajao.com, they get a 301 redirect to https://shajao.com. This is a good thing: Everyone should use HTTPS instead of HTTP. If you tell those third-party web sites to use HTTPS, it should work -- make sure to type in your web site URL starting with https://. With that said, if you really want to allow traffic over HTTP, you can turn off the option "Always Use HTTPS" in your Cloudflare settings.

Force https for single directory on my server with nginx config

I am trying to force SSL for a single subdirectory on my server by placing a rewrite rule in my nginx config file.
So, for example, when a user goes to example.com/billing or example.com/billing/user they are taken to https://example.com/billing or https://example.com/billing/user.
I have an SSL certificate installed etc. Here is a rule in my server block for nginx:
#billing location
location /billing/ {
if (!-e $request_filename){
rewrite ^/billing/(.*)$ /billing/index.php?request=$1 last;
}
}
Is there a way I can modify this rule to include forcing https?
I hope you have two server blocks one for http and other of http SSL connection; in your http server block adding redirect to https inside /billing/ location block will solve the issue.
server {
listen 80;
location /billing/ {
# 301 for permanent redirect
return 301 https://$host$request_uri;
}
}

Difference HTTP Redirect vs Reverse Proxy in NGINX

I'm having some difficulty in understanding the difference between reverse proxy (i.e. using proxy_pass directive with a given upstream server) and a 301 permanent redirect. How are they similar/different?
Reverse Proxy
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com:8080;
}
server {
location / {
proxy_pass http://backend;
}
}
HHTP Redirect
Apache Example: http://www.inmotionhosting.com/support/website/htaccess/redirect-without-changing-url
NGINX example:
server {
listen 80;
server_name domain1.com;
return 301 $scheme://domain2.com$request_uri;
}
Hence, it seems that both approaches have no difference from an end-user perspective. I want to ensure efficient bandwidth usage, while utilizing SSL. Currently, the app server uses its own self-signed SSL certificate with Nginx. What is the recommended approach for redirecting users from a website hosted by a standard web hosting company (hostgator, godaddy, etc.) to a separate server app server?
With a redirect the server tells the client to look elsewhere for the resource. The client will be aware of this new location. The new location must be reachable from the client.
A reverse proxy instead forwards the request of the client to some other location itself and sends the response from this location back to the client. This means that the client is not aware of the new location and that the new location does not need to be directly reachable by the client.