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

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 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.

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!