# How to setup local domains and sub-domains on your Mac using Nginx

Hi there.

Setting up local domains can be a serious pain. In this article, I will be providing insights into the process I have taken to set up domains and sub-domains locally using Nginx on MacOs (BigSur).

## Step 1 - Edit your hosts file

On your terminal, open your hosts file in an editor of your choice. 

If you have Vscode, you can run 

```
sudo code /etc/hosts 
```

For sublime text,

```
sudo subl /etc/hosts
```

For nano,

```
sudo nano /etc/hosts
```

For vi/vim
```
sudo vi /etc/hosts
```

The reason for adding `sudo` is to ensure you are able to save after your edit.

Your hosts file should look like this,

```
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1	localhost
255.255.255.255	broadcasthost
::1             localhost
```
Now, you need to update the file with the domain and sub-domain you require. For this article, we will be adding `example.test` as the main domain and `index.example.test` as the sub-domain. Both should be pointing to the loopback address `127.0.0.1 `.

After edit, we should have this,

```
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1	localhost
255.255.255.255	broadcasthost
::1             localhost
127.0.0.1  example.test # Update here
127.0.0.1  index.example.test # Update here

```
You can save and move to next step.

## Step 2 - Download and Install Nginx

To download and install Nginx, you can run the command

`brew install nginx`

For further instructions on installation, you can view the brew formulae https://formulae.brew.sh/formula/nginx .

Note the path to your nginx installation, so as to be able to locate the nginx configuration. In my case, the path to my nginx configuration exists at `/opt/homebrew/etc/nginx/nginx.conf`

After successful installation, you can move to the next step.

## Step 3 - Edit your Nginx configuration

To open the nginx configuration for edit, I use vscode, 

```
code /opt/homebrew/etc/nginx/nginx.conf
```

It should look like this,

```
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       8080;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
    include servers/*;
}
```

The following should be included in `nginx.conf`

```
server {
        listen      80;
        listen      [ :: ]:80;
        server_name  example.test *.example.test;
        access_log /var/log/nginx/example.test.access.log;
        error_log /var/log/nginx/example.test.error.log;

        location / {
            proxy_pass   http://127.0.0.1:8000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }

    server {
        listen      80;
        listen      [ :: ]:80;
        server_name  index.example.test;
        access_log /var/log/nginx/example.test.access.log;
        error_log /var/log/nginx/example.test.error.log;

        location / {
            proxy_pass   http://127.0.0.1:8000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }
```
NOTE: the `proxy_pass` should refer to the port your project is running on. In my case, my project is running on port `8000`.

In this case, my domain and sub-domain are pointing to the same project. This means the project handles the route redirection based on the domain. But if for your project the domain and sub-domain run on separate ports, then you should configure the `proxy_pass` with the correct port number.

The final `nginx.conf` should look like this,

```
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       8080;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }

    server {
        listen      80;
        listen      [ :: ]:80;
        server_name  example.test *.example.test;
        access_log /var/log/nginx/example.test.access.log;
        error_log /var/log/nginx/example.test.error.log;

        location / {
            proxy_pass   http://127.0.0.1:8000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }

    server {
        listen      80;
        listen      [ :: ]:80;
        server_name  index.example.test;
        access_log /var/log/nginx/example.test.access.log;
        error_log /var/log/nginx/example.test.error.log;

        location / {
            proxy_pass   http://127.0.0.1:8000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
    include servers/*;
}
```

## Step 4 - Create log folder

A common cause for error when working with this is that there may not be an `nginx` folder under the `/var/log/` path. To create the folder, you can run

```
mkdir /var/log/nginx
```

## Step 5 - Start up Nginx

Before we startup Nginx, from my experience, it is better to have the nginx property list file in the ~/Library/LaunchDaemons folder, but default you'd have it in the ~/Library/LaunchAgents folder, hence we need to move it, with the following command

```
sudo mv ~/Library/LaunchAgents/homebrew.mxcl.nginx.plist /Library/LaunchDaemons/
```
Then, modify the file permissions to allow loading

```
sudo chown root:wheel /Library/LaunchDaemons/homebrew.mxcl.nginx.plist
```

Finally, load the `plist` file to startup nginx,

```
sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.nginx.plist
```

To confirm nginx has started you should run,

```
sudo brew services list
```

A successfully started nginx response should look like this,

```
Name  Status   User  File
nginx  started  root  /Library/LaunchDaemons/homebrew.mxcl.nginx.plist
```

If the Status show `error` you can run `nginx -T` to test your configuration file and check if there are any errors.

## Step 6 - Test your setup

Now you can startup your project on the port you have defined in the `nginx.conf` `proxy_pass`.

And open up your browser and visit your url at [http://example.test](http://example.test).

You should see your project come up!

That's it!

Feel free to comment below if this setup works or doesn't work for you. 

I'll see you in the comments. 

Cheers!



