Redirect non-www to www over SSL with Nginx
Solution 1
You are missing listen
directive in file default-ssl.conf
. Add listen 443;
in this directive
server {
server_name example.com;
return 301 https://www.example.com$request_uri;
}
By default, if you omit this directive, nginx assume that you want listen on port 80. Here the documentation of this default behavior.
Edit: Thanks for comment from @TeroKilkanen.
Here the complete config for your default-ssl.conf
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /srv/www/example.com/keys/ssl.crt;
ssl_certificate_key /srv/www/example.com/keys/www.example.com.key;
return 301 https://www.example.com$request_uri;
}
Sidenote: You can replace ssl on;
directive with listen 443 ssl;
as recommendation from nginx documentation.
Solution 2
Just throw in an if statement and you should be on your way. I checked the results in curl.exe -I and all cases besides https://www.example.com get treated as 301. SSL is tricky because it gets checked before you get 301 URL redirection. Hence, you get certificate errors.
Personally, I like removing the www's from the domain but I wrote my code below to answer your question.
server {
listen 443 ssl;
listen [::]:443 ssl; # IPV6
server_name example.com www.example.com; # List all variations here
# If the domain is https://example.com, lets fix it!
if ($host = 'example.com') {
return 301 https://www.example.com$request_uri;
}
# If the domain is https://www.example.com, it's OK! No changes necessary!
... # SSL .pem stuff
...
}
server {
listen 80;
listen [::]:80;
# If the domain is http://example.com or http://www.example.com, then let's change it to https!
server_name example.com www.example.com;
return 301 https://www.example.com$request_uri;
}
Solution 3
The way I do it is to use an if statement inside the ssl server block that redirects to https of www
ssl_certificate /srv/www/example.com/keys/ssl.crt;
ssl_certificate_key /srv/www/example.com/keys/www.example.com.key;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers AES128-SHA:RC4-MD5:ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:RSA+3DES:!ADH:!AECDH:!MD5:AES128-SHA;
ssl_prefer_server_ciphers on;
client_max_body_size 20M;
upstream app_server_ssl {
server unix:/tmp/unicorn.sock fail_timeout=0;
}
server {
server_name example.com;
return 301 https://www.example.com$request_uri
}
server {
listen 443 default_server ssl;
server_name www.example.com;
# redirect https://example.com to https://www.example.com
# mainly for SEO purposes etc
#we will use a variable to do that
set $redirect_var 0;
if ($host = 'example.com') {
set $redirect_var 1;
}
if ($host = 'www.example.com') {
set $redirect_var 1;
}
if ($redirect_var = 1) {
return 301 https://www.example.com$request_uri;
}
try_files $uri/index.html $uri.html $uri @app;
# CVE-2013-2028 http://mailman.nginx.org/pipermail/nginx-announce/2013/000112.html
if ($http_transfer_encoding ~* chunked) {
return 444;
}
location @app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server_ssl;
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root /home/app/example/current/public;
}
}
Of course, whenever you want to use an if statement in an nginx config file; you should have read: https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/
Solution 4
Its 2018 now and i figured to give this one a renewed shot in case some one is looking for a simple solution.
My take on this as a relatively new-comer is to make things as simple as possible. Basically you wish to redirect both http://example.com and https://example.com to https://www.example.com. And that you only succeed in redirecting http://example.com
This is quite a straightforward operation requiring only two server blocks (i will demonstrate this briefly in a single config file)
# 1. Server block to redirect all non-www and/or non-https to https://www
server {
# listen to the standard http port 80
listen 80;
# Now, since you want to route https://example.com to http://www.example.com....
# you need to get this block to listen on https port 443 as well
# alternative to defining 'ssl on' is to put it with listen 443
listen 443 ssl;
# define server_name
server_name example.com *.example.com;
# DO NOT (!) forget your ssl certificate and key
ssl_certificate PATH_TO_YOUR_CRT_FILE;
ssl_certificate_key PATH_TO_YOUR_KEY_FILE;
# permanent redirect
return 301 https://www.example.com$request_uri;
# hard coded example.com for legibility
}
# end of server block 1. nearly there....
# 2. Server block for the www (primary) domain
# note that this is the block that will ultimately deliver content
server {
# define your server name
server_name www.example.com;
# this block only cares about https port 443
listen 443 ssl;
# DO NOT (!) forget your ssl certificate and key
ssl_certificate PATH_TO_YOUR_CRT_FILE;
ssl_certificate_key PATH_TO_YOUR_KEY_FILE;
# define your logging .. access , error , and the usual
# and of course define your config that actually points to your service
# i.e. location / { include proxy_params; proxy_pass PATH_TO_SOME_SOCKET; }
}
# End of block 2.
# voilà!
Now both http://example.com and https://example.com should redirect to https://www.example.com. Basically this setup redirects everything non-www and/or non-https to https://www.
Solution 5
In my case, to redirect from http://example.com to https://www.example.com. I configure my nginx with "IF condition" like this and it works for me.
server {
listen 80;
server_name example.com;
if ($host = example.com) {
return 301 https://www.$host$request_uri;
}
location / {
root html;
index index.php index.html index.htm;
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
....
server {
server_name www.example.com;
listen 80;
return 404;
}
Related videos on Youtube
Hicham
Updated on September 18, 2022Comments
-
Hicham over 1 year
When doing a "mvn clean install" on a maven project, on which I don't have any dependencies problem, Some classes on sub-packages doesn't recognize other classes on upper packages (on the same project),
An illustration of the situation is on the image:
So, ContractOperation.java can't import ContractData.java.
The error is : src/main/java/com/mycompany/product/ws/server/ops/ContractOperation.java:[69,31] cannot find symbol symbol : class ContractData location: package com.mycompany.product.ws.helpers
Any idea please. Thanks in advise.
-
Sandip Subedi about 7 yearsWhat is the point of creating 2 conf files ?
-
Thomas V. about 7 yearsSeparation of concerns, the non SSL config was so small that it seemed best to separate it from the SSL only config.
-
-
Tero Kilkanen over 9 yearsYou also need to set up
ssl_certificate
andssl_certificate_key
directives in this block, and uselisten 443 ssl;
so that it is an SSL vhost. -
masegaloeh over 9 yearsPlease post contents of current
default-ssl.conf
. Maybe some typo or reorder issue caused that. -
Thomas V. over 9 yearsThis is embarrassing :\ The culprit was a duplicate nginx configuration in /etc/nginx/sites-enabled, /etc/nginx/sites-enabled/default-ssl.backup was interfering with any redirects in default-ssl. Silly error.
-
Lukas Oppermann over 7 yearsYou are missing a
;
in the second server block in the if clause. It should bereturn 301 https://www.example.com$request_uri;
-
Lukas Oppermann over 7 yearsHowever, would that even work? Can it listen for
http
on443
? -
lfender6445 over 7 yearsyou are right, i am missing listen 80, ive added it in.
-
Karl.S almost 7 yearsthe condition
if ($host = 'www.example.com')
is not necessary. -
vladkras almost 7 yearsso I had to issue 2 certs: for www-domain and for non-www one
-
CP3O over 5 yearsHow can this be achieved on say port 5007: example.com:5007 to example.com:5007