Nginx rewrite non-www-prefixed domain to www-prefixed domain - nginx

I see the Nginx HttpRewriteModule documentation has an example to rewrite a www-prefixed domain to a non-www-prefixed domain:
if ($host ~* www\.(.*)) {
set $host_without_www $1;
rewrite ^(.*)$ http://$host_without_www$1 permanent; # $1 contains '/foo', not 'www.mydomain.com/foo'
}
How can I do the reverse-- rewrite a non-www-prefixed domain to a www-prefixed domain? I thought maybe I could do something like the following but Nginx doesn't like the nested if statement.
if ($host !~* ^www\.) { # check if host doesn't start with www.
if ($host ~* ([a-z0-9]+\.[a-z0-9]+)) { # check host is of the form xxx.xxx (i.e. no subdomain)
set $host_with_www www.$1;
rewrite ^(.*)$ http://$host_with_www$1 permanent;
}
}
Also I wanted this to work for any domain name without explicitly telling Nginx to rewrite domain1.com -> www.domain1.com, domain2.com -> www.domain2.com, etc. since I have a large number of domains to rewrite.

As noted in the Nginx documentation, you should avoid using the if directive in Nginx where possible, because as soon as you have an if in your configuration your server needs to evaluate every single request to decide whether to match that if or not.
A better solution would be multiple server directives.
server {
listen 80;
server_name website.com;
return 301 $scheme://www.website.com$request_uri;
}
server {
listen 80;
server_name www.website.com;
...
}
If you're trying to serve an SSL (HTTPS) enabled site, you got more or less three different options.
Set up multiple IP addresses having each server directive listening on their own IP (or different ports if that's an option for you). This options needs SSL certificates for both website.com and www.website.com, so either you have a wild card certificate, a UNI certificate (multiple domains) or just plainly two different certificates.
Do the rewrite in the application.
Use the dreaded if directive.
There is also an option to use SNI, but I'm not sure this is fully supported as of now.

if ($host !~* ^www\.) {
rewrite ^(.*)$ http://www.$host$1 permanent;
}

Well I guess I don't really need the outer "if" statement since I'm only checking for domains of the form xxx.xxx anyways. The following works for me, though it's not robust. Let me know if there is a better solution.
if ($host ~* ^([a-z0-9\-]+\.(com|net|org))$) {
set $host_with_www www.$1;
rewrite ^(.*)$ http://$host_with_www$1 permanent;
}
Edit: Added hyphen to the regular expression since it is a valid character in a hostname.

if ($host ~* ^[^.]+\.[^.]+$) {
rewrite ^(.*)$ https://www.$host$1 permanent;
}
It's only possible to get valid hostnames because the request will never make it to your server otherwise, so there's no need to build your own validation logic.

The nginx documentation cautions against the use of if for rewriting. Please see the link here: http://wiki.nginx.org/Pitfalls#Server_Name

HTTP & HTTPS without if conditions:
server {
listen 80;
listen 443;
server_name website.com;
return 301 $scheme://www.website.com$request_uri;
}
server {
listen 80;
listen 443 default_server ssl;
server_name www.website.com;
# Your config goes here #
}

Solution for multiple domains, working on nginx 1.17 for me:
server {
listen 80;
server_name .example.com;
set $host_with_www $host;
if ($host !~* www\.(.*)) {
set $host_with_www www.$host;
}
return 301 https://$host_with_www$request_uri;
}
In this config example additionally rewrites HTTP on HTTPS, if you don't want rewrite — replace https:// with http:// in return string.
If you want keep protocol — use $scheme variable.

Related

nginx - proxy_pass with return 301 redirect

I am building my nginx configuration with automated tools like nginx-proxy on docker. There they let me add a custom line inside the location directive.
Simply, I want www.example.com to be 301 redirected to example.com, or generally both http://www.example.com and https://www.example.com should be 301 redirected to https://example.com.
The automated configuration creation results as such:
server {
...
server_name www.example.com;
listen 443 ssl http2 ;
location / {
proxy_pass http://<upstream>;
return 301 $scheme://example.com;
}
}
I notice that there are proxy_pass syntax before return 301 ..., and since its creation is automated, I don't think I can easily modify that (i.e. to make return 301 appear before the proxy_pass syntax.
From nginx documentation:
proxy_pass
Sets the protocol and address of a proxied server and an optional URI to which a location should be mapped.
So, nginx-wise question, since it said "sets", will the 301 redirect be done correcly, even if the redirect came after the proxy_pass syntax ?
From the comment by RichardSmith, it is said that the return syntax will be evaluated first and hence proxy_pass is completely ignored.

Nginx - Reverse proxy everything after location specification

I'm trying to use nginx to reverse proxy a specific location specification, as below:
server {
listen 80;
server_name example.com;
location /example {
proxy_pass http://localhost:8080/test;
}
}
Now, when I try and access a resource at http://example.com/example/css/styles.css I expect it to try and access http://localhost:8080/test/css/styles.css. But alas - I get a 404 from nginx.
When I try and access http://example.com/example it shows me what's on http://localhost:8080/test (so I know the base url segment is working) minus anything being imported into that page from a relative url (e.g. styles and JS files)
How do I get the reverse proxy to work with child url segments?
Per Ivan's comment on the original question, here is a working configuration with trailing slashes added:
server {
listen 80;
server_name example.com;
location /example/ {
proxy_pass http://localhost:8080/test/;
}
}

nginx redirect twice from https to http

i have 2 servers, one has ssl and i config it like this,
in the server with SSL certification(which is https:// www.example.com):
location ~^/abc/.* {
proxy_pass http://www.example.com:8214/
}
in another server(which is http:// www.anotherExample.com):
server {
listen 8214;
server_name www.anotherExample.com;
rewrite ^/(.*)$ http://www.anotherExample.com:8080/$1 permanent;
}
and after access https:// www.example.com/abc/api/getGroup
it can't redirect to http:// www.anotherExample.com:8080/api/getGroup
Anything wrong???
There are a couple of things you could do to improve your configuration.
location ^~ /abc/ {
proxy_pass http://www.example.com:8214$uri;
#You should have other directives set here as well.
}
Also, consider setting up an upstream.
Then, for your server block:
server{
listen 8124;
server_name www.anotherExample.com;
rewrite ^/abc/(.*)$ http://www.anotherExample.com:8080/$1 permanent;
}
server{
listen 8080;
server_name www.anotherExample.com;
location ^~ /api/ {
#your_config_here
}
}
The explanation:
In your first location block, you shouldn't have .* in the expression. Nginx will match this for you. Then, when you're proxying, you can explicitly tell Nginx to send the URI as well.
Next, you're sending the URI www.anotherExample.com:8124, which includes /abc/, so you want to extract everything after that.
Lastly, because you've rewritten it to point to 8080 port, you'll need to define a separate server block for this.
I don't know what you're aiming to achieve, but so much proxying and redirects isn't necessary in most cases, and might lead to poor performance. Another consideration that you should take into account is you're sending unencrypted information to anotherExample.com, which, if not on the same local network, might be a security vulnerability.

nginx rewrite for subsubdomains to subdomains

We have an application where we use subdomains for each of our customer's installations. so we have customer1.ourapp.com, customer2.ourapp.com, customer3.ourapp.com and so on.
Because of security we want to redirect all http to https, since we have a wildcard SSL certificate.
Also, some customers are not that tech savvy and add www to their domain name, so then you get things like: http://www.customer1.ourapp.com or https://www.customer1.ourapp.com. In those cases the SSL certificate isn't valid because of the subsubdomain.
I'm trying to write the vhost config for nginx to make the correct redirect in these two cases. I got the http to https redirect to work with:
server {
listen 80;
server_name *.ourapp.com;
#Rewrite all nonssl requests to ssl.
return 301 https://$host$request_uri$is_args$args;
}
correct url's use:
server {
listen 443;
server_name *.ourapp.com;
#Rest of config
}
Made an attempt for the subsub domains, but it's not matching:
server {
server_name "~^(.*)\.(.*)\.ourapp\.com$";
return 301 https://$2.ourapp.com$request_uri;
}
Any idea how to get this working?
Wildcarded server takes precedence over regexp'ed one and matches 'www...' too.
You can use one definition for both cases:
server_name ~ ^(?:.*\.)?(.*)\.ourapp\.com$;
return 301 https://$1.ourapp.com$request_uri;

Nginx: How to rewrite only when all location blocks fail?

I have a web site which needs to be using https connection for pretty much the whole site except a few locations which I need to be served via http. To do that I have two servers setup in nginx config. One is for non-secure and one for secure connections. However for the non-secure server, I want to be able to rewrite to the secure web one only when none of the location blocks are validated.
Is that possible? If yes, how?
Structure of my nginx config:
server {
listen 80;
...
location /foo1 { ... }
location /foo2 { ... }
# i can't get this rewrite to work only when all location blocks fail
rewrite ^/(.*) https://foo.com/$1 permanent;
}
server {
listen 443;
...
}
Thanx
Add to the end of the server block:
location / {
rewrite ^/(.*) https://foo.com/$1 permanent;
}

Resources