Nginx: rewrite all subdomains to www except for one? - configuration

Suppose I have a pair of servers that, due to the way things get deployed, need to use the same nginx.conf. One of the servers is at staging.someserver.com, and the other is at www.someserver.com.
What I want is a single rewrite line or an if condition that will redirect everything on the domain (mainly www and non-www on http, and non-www on https) to https://www.someserver.com/, and for http://staging.someserver.com/ to redirect to https://staging.someserver.com/, but not to https://www.someserver.com/. How can I do this?

This is a trick on Nginx configuration : The first server block of HTTP block will be the "default" configuration.
So you can redirect in the first server block everything, and create another server block with your www. configuration !
Thats the best solution.

I don't know if this is strictly the best way to do this, but I figured something out:
server {
listen 443 default deferred;
# ...
if ($host !~ (staging)|(www).*)
rewrite ^(.*) https://www.someserver.com$1 permanent;
}
}
server {
listen 80;
# ...
if ($host !~ staging.*) {
rewrite ^(.*) https://www.someserver.com$1 permanent;
}
if ($host ~ staging.*) {
rewrite ^(.*) https://staging.someserver.com$1 permanent;
}
}

Related

nginx: location of static content within a subfolder

I'm trying to set up an nginx server to serve a React app at the address http://mydomain/memorygame
Currently I have the following nginx routing config:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/mydomain.com;
index index.html;
server_name mydomain.com www.mydomain.com;
location /memorygame {
root /var/www/mydomain.com/memorygame;
try_files $uri /$uri $uri/ /index.html $uri/index.html =404;
location ~* \.(css|js)$ {
try_files $uri /$uri =404;
}
}
The css files are stored under /var/www/mydomain.com/memorygame, in the index.html the link is /static/css/main.d5dd0bd5.css.
The index.html load fine, but the css requests aren't routed to where I want them. If I take out "/index.html" from try_files, the css loads, but index.html doesn't. How can I make them both work at the same time?
(My guess is that part of the problem is that css request issued goes to mydomain.com/static/css/style.css instead of mydomain.com/memorygame/static/css/style.css, but I might be wrong. If I'm correct, is there a way to auto-prepend the /static/css/style.css to be relative to the mydomain.com/memorygame folder?)
Thanks in advance!
I figured it out myself. Deleting everything out of the location /memorygame block did the trick. Nginx does what I was trying to achieve by default.

NGINX, proxy_pass and SPA routing in HTML5 mode

I have NGINX set up as a reverse proxy for a virtual network of docker containers running itself as a container. One of these containers serves an Angular 4 based SPA with client-side routing in HTML5 mode.
The application is mapped to location / on NGINX, so that http://server/ brings you to the SPA home screen.
server {
listen 80;
...
location / {
proxy_pass http://spa-server/;
}
location /other/ {
proxy_pass http://other/;
}
...
}
The Angular router changes the URL to http://server/home or other routes when navigating within the SPA.
However, when I try to access these URLs directly, a 404 is returned. This error originates from the spa-server, because it obviously does not have any content for these routes.
The examples I found for configuring NGINX to support this scenario always assume that the SPA's static content is served directly from NGINX and thus try_files is a viable option.
How is it possible to forward any unknown URLs to the SPA so that it can handle them itself?
The solution that works for me is to add the directives proxy_intercept_errors and error_page to the location / in NGINX:
server {
listen 80;
...
location / {
proxy_pass http://spa-server/;
proxy_intercept_errors on;
error_page 404 = /index.html;
}
location /other/ {
proxy_pass http://other/;
}
...
}
Now, NGINX will return the /index.html i.e. the SPA from the spa-server whenever an unknown URL is requested. Still, the URL is available to Angular and the router will immediately resolve it within the SPA.
Of course, now the SPA is responsible for handling "real" 404s. Fortunately, this is not a problem and a good practice within the SPA anyway.
UPDATE: Thanks to #dan

How do I redirect www traffic without triggering browsers SSL check?

I have a valid certificate for example.com. If users go to my site at http://example.com, they get redirected to https://example.com and all is good. If they go to https://example.com, all is good. If they even go to http://www.example.com, they get redirected to https://example.com and all is good.
However, if they go to https://www.example.com, Chrome triggers its SSL warning before I can redirect and tells the user to not trust this site. I don't have this problem in Safari or Firefox.
Here's my nginx configuration. What am I doing wrong?
```
# Configuration for redirecting non-ssl to ssl;
server {
listen *:80;
listen [::]:80;
server_name example.com;
return 301 https://example.com$request_uri;
}
# Configuration for redirecting www to non-www;
server {
server_name www.example.com;
ssl_certificate ssl/ssl_cert.crt;
ssl_certificate_key ssl/ssl_key.key;
listen *:80;
listen *:443 ssl spdy;
listen [::]:80 ipv6only=on;
listen [::]:443 ssl spdy ipv6only=on;
return 301 https://example.com$request_uri;
}
server {
listen *:443 ssl spdy;
listen [::]:443 ssl spdy;
ssl_certificate ssl/ssl_cert.crt;
ssl_certificate_key ssl/ssl_key.key;
server_name example.com;
}
```
EDIT: I see that this is a problematic configuration because the second block will look at the certs. What's the proper way to set this up with a cert that reads from "example.com" rather than "www.example.com"?
If your certificate is for example.com only and not for www.example.com then any access to www.example.com will trigger a certificate warning, no matter if you want just redirect it or not. Redirection is done at the HTTP level and before it talks HTTP it first does the SSL handshake (which triggers the problem), because HTTPS is just HTTP inside SSL.
And before you ask, tricks with DNS (like CNAME) will not help either because the browser will compare the certificate against the name in the URL, not against possible DNS alias names. There is simply no way around getting a proper certificate.

Catch-all configuration except for www.website.com?

Using Nginx, I'm trying to configure my server to accept all domains that point to the IP of my server, by showing them a specific website, but when accessing the www.example.com (main website), I'd show an other content.
Here's what I did so far:
server {
// Redirect www to non-www
listen 80;
server_name www.example.com;
return 301 $scheme://example.com$request_uri;
}
server {
listen 80;
server_name example.com;
// rest of the configuration
}
server {
// Catch all
listen 80 default_server;
// I also tried
// server_name _;
// Without any luck.
// Rest of the configuration
}
The problem with this configuration is that every request made to this server not being www.example.com or example.com is took under example.com server configuration, not the catch all.
I'd like to cath only www.example.com/example.com in the first two configurations, and all the others in the last configuration.
I suggest putting your server on top of the file :)
I think nginx wants default servers to be on top of -a- file.
I have really much files on my server, but there is one with a default server as first server declaration, and that works.

nginx trailing slash issues

I'm googling a lot and found several workarounds, but you have to define every single directory.
On Apache: example.com/hi -> example.com/hi/
On nginx: example.com/hi -> Firefox can't establish a connection to the server at example.com:8888
where 8888 is what Apache is listening on (nginx's :80 -> localhost:8888)
Any ideas how to fix this and have it just forward normally like folder?
I had a similar problem with varnish and nginx (varnish on port 80 proxying to nginx listening on 8080) and needed to add "port_in_redirect off;" ... server_name_in_redirect needed to stay on so nginx knew which host it was handling.
The following should do the trick, but it needs more thought/work, because only a single location block will get used at a time:
location ~ ^(.*[^/])$ {
if (-d $document_root/$1) {
rewrite ^(.*)$ $1/ permanent;
}
}
(not tested)
You can set "server_name_in_redirect off" on your server section
server{
listen 80 default;
server_name localhost;
server_name_in_redirect off;
...
...
}
That will do the trick ;-)
HTH.
Edit: Just format.
This is the magic that works best for me:
try_files $uri $uri/ #redirect;
location #redirect {
if ($uri !~ '/$') {
return 301 $uri/$is_args$args;
}
}
The 'if' statement here is safe per: http://wiki.nginx.org/IfIsEvil