Nginx – Redirecting from www to no www in nginx reverse proxy

301-redirectdjangonginxredirect

I'm developing a Django app with nginx reverse proxy and gunicorn application server. Being a newbie web developer, I need help with redirecting www traffic to no www at the webserver level. I'm currently doing the same at the middleware level inside the app, but need to improve performance.

Currently my nginx virtual host file is laid out as follows:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=100m inactive=6m;

upstream my_server {
    server unix:/home/myuser/myproject/myfolder/myproject.sock  fail_timeout=0;
}

server {
    listen 80;
    server_name example.com www.example.com;

    # a bunch of 'location' blocks e.g. 'location /' or 'location @http_proxy_to_app', etc. 

}

server {

    listen 443 ssl;
    server_name example.com www.example.com;

    # SSL related stuff

    # a bunch of 'location' blocks e.g. 'location /' or 'location @http_proxy_to_app', etc.

}

How would I redirect www to no-www in this scenario? Most of the examples I've seen explain that I need to add one more dedicated server block at the top like so:

server {
        server_name www.example.com;
        return 301 $scheme://example.com$request_uri;
}

Other suggestions have been to include something like the following inside my server blocks (inside location /). I'm unsure whether this would be compatible with reverse proxy related code:

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

Being a newbie, I wanted to confirm which practice to use (and why) given how my nginx file is currently laid out, hence this question.


Here's what my actual config looks like:

server {

server_name example.com;
listen 443 ssl;

#ssl_certificate /etc/ssl/certs/ssl-bundle.crt;
ssl_certificate /etc/nginx/ssl/cert_chain.crt;
#ssl_certificate_key /etc/ssl/myserver.key;
ssl_certificate_key /etc/nginx/ssl/server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
#ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
#ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5; 
ssl_session_cache shared:SSL:1250m;
ssl_session_timeout 180m;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_dhparam /etc/ssl/certs/dhparam.pem;

ssl_stapling on;
ssl_stapling_verify on;
#ssl_trusted_certificate /etc/ssl/COMODO_DV_SHA-2_under_SHA-2root.crt;
ssl_trusted_certificate /etc/nginx/ssl/example_com.ca-bundle;

# write error log file for https errors:
error_log /var/log/nginx/https-error_log warn;

location ~* \.(?:ico|css|js|gif|jpe?g|png)$ { root /home/myuser/myproj/myapp; expires 24h; add_header Vary Accept-Encoding; access_log off; }

.... more config to follow ...

}

Best Answer

The preferred setup for a web server handling http://example.com, https://example.com, http://www.example.com and https://www.example.com is the following when the example.com domain and https are preferred:

server {
    server_name example.com www.example.com;

    listen 80;

    return 301 https://example.com$request_uri;
}

server {
    server_name www.example.com;

    listen 443 ssl;

    return 301 https://example.com$request_uri;
}

server {
    server_name example.com;

    listen 443 ssl;

    ... actual server configuration ...
}

In this setup, there is only one actual URL https://example.com for reaching the site content, which is good for indexing. In your current setup, the same site content is available with all URLs, which causes duplicate issues with Google.

Defining a separate virtual host for the redirects is the preferred way, because evaluating the if statement causes duplicate work for requests already coming in to the correct virtual host block.