Search code examples
nginxconfigurationserverconfiguration-filesnginx-location

Nginx is serving the default content instead of my content


I have a nginx version: nginx/1.10.3 (Ubuntu) running on Ubuntu 16.04.2 LTS.

I use nginx to serve static files, bundles generated by webpack, but that's irrelenvant.

What I want to achieve is this:

On example.com I want to serve /home/bundles/main/index.html. I can do this.

On projects.example.com/project_1 I want to serve /home/bundles/project_1/index.html.

On projects.example.com/project_2 I want to serve /home/bundles/project_2/index.html.

The last two, I can't do. When I go to projects.example.com/project_1 or projects.example.com/project_2 I am served the default nginx page.

To make things more confusing /etc/nginx/sites-enabled/default is entirely commented out.

Additionally, if in the location block of projects.example.com I replace, for example, project_1 with /, I will be served that specific project, but then I will have no way of serving the other.

Bellow, I will show you my nginx configuration


server {
    listen 80;
    server_name example.com;

    location / {
        root /home/bundles/main;
        try_files $uri /index.html;
    }

    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name example.com;

    location / {
        root /home/bundles/main;
        try_files $uri /index.html;
    }

    ssl_certificate ...
    ssl_certificate_key ...
}

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

    location /project_1 {
        root /home/bundles/project_1;
        try_files $uri /index.html;
    }

    location /project_2 {
        root /home/bundles/project_2;
        try_files $uri /index.html;
    }

    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name projects.example.com;

    location /project_1 {
        root /home/bundles/project_1;
        try_files $uri /index.html;
    }

    location /project_2 {
        root /home/bundles/project_2;
        try_files $uri /index.html;
    }

    ssl_certificate ...
    ssl_certificate_key ...
}

Thank you for your help!


EDIT

My Answer

The solution I found was to change the root with alias.

server {
    listen 80;
    server_name example.com;

    location / {
        root /home/bundles/main;
        try_files $uri /index.html;
    }

    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name example.com;

    location / {
        root /home/bundles/main;
        try_files $uri /index.html;
    }

    ssl_certificate ...
    ssl_certificate_key ...
}

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

    location /project_1 {
        alias /home/bundles/project_1;
        index index.html;
    }

    location /project_2 {
        alias /home/bundles/project_2;
        index index.html;
    }

    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name projects.example.com;

    location /project_1 {
        alias /home/bundles/project_1;
        index index.html;
    }

    location /project_2 {
        alias /home/bundles/project_2;
        index index.html;
    }

    ssl_certificate ...
    ssl_certificate_key ...
}

The solution is based on these two answers. The first answer showing how to solve the problem and the second answer providing a explanation as to why alias works and root does not.

To quote @treecoder

In case of the root directive, full path is appended to the root including the location part, whereas in case of the alias directive, only the portion of the path NOT including the location part is appended to the alias.

In my particular case, this would translate like this;

With root, the path nginx would try to access would be /home/bundles/project_1/project_1.

With alias it accesses the correct path, /home/bundles/project_1.

Going back one level, for example, saying:

root /home/bundles/ is not really a option either. That is because my projects are not actually called project_1 and project_2. The actual structure is more similar to this.

In /bundles I have the directories project_a and project_b. I want to route project_1 to the project_a directory and project_2 to the project_b directory.

That is why I used alias.

I hope this helps.


Solution

  • You have:

    location /project_1 {
        root /home/bundles/project_1;
        try_files $uri /index.html;
    }
    

    So the root is only defined for URIs that begin with /project_1. For any other URI, the default root will be used.

    If you present the URI /project_1/ (with a trailing /), assuming that the default index directive is in force, nginx should return your /project_1/index.html content.

    However, the URI /project_1 is not found - so /index.html is returned instead. The URI /index.html does not begin with /project_1, so the default root is used.


    If you want the URI /project_1 to work as expected, and the default action to go to the project's index.html file, change the try_files directive.

    location /project_1 {
        root /home/bundles/project_1;
        try_files $uri $uri/ /project_1/index.html;
    }
    

    See this document for more.


    As both projects share a common root, you could simplify as follows:

    server {
        listen 80;
        server_name projects.example.com;
    
        root /home/bundles
        index index.html;
    
        location /project_1 {
            try_files $uri $uri/ /project_1/index.html;
        }
        location /project_2 {
            try_files $uri $uri/ /project_2/index.html;
        }
        location / {
            deny all;
        }
    }
    

    I added the index directive to avoid relying on the default value (which is the same), and a location block to deny access to areas outside of the projects.