Increasing productivity


Speed Up Magento2 on Mac! - Installing Web Server

Speed Up Magento2 on Mac! - Installing Web Server

Once Magento2 is installed, we are going to configure our web server.

Steps to follow

Nginx Conf


Magento offers you a base file for Nginx with the necessary configuration so that magento2 can run on the Server. That means we have to copy the file to our magento.conf file. We can do that from the terminal by executing the following:

    cp vendor/magento/magento-base/nginx.conf.sample config/nginx/includes/magento.conf


Then we define our parameters SSL (Secure Sockets Layer).. We edit our file "config/nginx/includes/ssl.conf" with the following content:

    ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;

add_header Strict-Transport-Security max-age=15768000;


For our backend container we only need to declare a server that listens to our non ssl port 80 and define the routes of our project. The content is as follows: (NOTE: "php" and "backend" refer to our containers declared in our docker-compose.yml file, they are practically the names of the services 😃)

    upstream fastcgi_backend {
server php:9000; #php (docker-compose.yml))

server {
listen 80;
listen [::]:80;
set $MAGE_ROOT /var/www/html;
include /etc/nginx/includes/magento.conf;

server {
listen 80;
listen [::]:80;
server_name backend; #backend (docker-compose.yml)
root /var/www/html;
client_max_body_size 20M;
location /pub/health_check.php {
fastcgi_pass fastcgi_backend;
fastcgi_buffers 1024 4k;
fastcgi_read_timeout 600s;
fastcgi_connect_timeout 600s;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;

location / {
deny all;


Our archive frontend.conf it has to have the ssl configuration where we also link our certificates. (NOTE: the path declared in our configuration file is the interpreted path or rather the path of our container, that is, Inside Docker). Varnish serves as Proxy.

The content of the file is as follows: (NOTE: "varnish" refers to our container declared in our docker-compose.yml file, it is practically the name of the service 😃)

    server {
listen 80;
listen [::]:80;
server_name default;
return 301 https://$host$request_uri;

server {
listen 443 ssl http2;
listen [::]:443;
server_name default;

include /etc/nginx/includes/ssl.conf;
ssl_certificate /etc/nginx/certs/certificate.crt;
ssl_certificate_key /etc/nginx/certs/key.key;
ssl_dhparam /etc/nginx/certs/dhparams.pem;

location / {
proxy_pass http://varnish:80; #varnish (docker-compose.yml)
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
send_timeout 600;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;

Varnish Conf


There are many sites on the internet that show possible vcl files for magento2. But if you do not know what varnish is or you are very familiar, you may be interested in the following links:

Well here we leave our suggestion.

    vcl 4.0;

import std;
# The minimal Varnish version is 4.0
# For SSL offloading, pass the following header in your proxy server or load balancer: 'X-Forwarded-Proto: https'

backend default {
.host = "backend";
.port = "80";
.first_byte_timeout = 600s;

acl purge {

sub vcl_recv {
set req.http.X-Actual-IP = regsub(req.http.X-Forwarded-For, "[, ].*$", "");
set req.http.X-Real-IP = regsub(req.http.X-Forwarded-For, "[, ].*$", "");

#if (std.ip(req.http.X-Actual-IP, "") !~ whitelist) {
#return (synth(303, "Access Denied"));

if (req.method == "PURGE") {
if (client.ip !~ purge) {
return (synth(405, "Method not allowed"));
# To use the X-Pool header for purging varnish during automated deployments, make sure the X-Pool header
# has been added to the response in your backend server config. This is used, for example, by the
# capistrano-magento2 gem for purging old content from varnish during it's deploy routine.
if (!req.http.X-Magento-Tags-Pattern && !req.http.X-Pool) {
return (synth(400, "X-Magento-Tags-Pattern or X-Pool header required"));
if (req.http.X-Magento-Tags-Pattern) {
ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern);
if (req.http.X-Pool) {
ban("obj.http.X-Pool ~ " + req.http.X-Pool);
return (synth(200, "Purged"));

if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);

# We only deal with GET and HEAD by default
if (req.method != "GET" && req.method != "HEAD") {
return (pass);

# Bypass shopping cart, checkout and search requests
if (req.url ~ "/checkout" || req.url ~ "/catalogsearch") {
return (pass);

# Bypass health check requests
if (req.url ~ "/pub/health_check.php") {
return (pass);

return (pass);

# Set initial grace period usage status
set req.http.grace = "none";

# normalize url in case of leading HTTP scheme and domain
set req.url = regsub(req.url, "^http[s]?://", "");

# collect all cookies

# Compression filter. See
if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") {
# No point in compressing these
unset req.http.Accept-Encoding;
} elsif (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} elsif (req.http.Accept-Encoding ~ "deflate" && req.http.user-agent !~ "MSIE") {
set req.http.Accept-Encoding = "deflate";
} else {
# unkown algorithm
unset req.http.Accept-Encoding;

# Remove Google gclid parameters to minimize the cache objects
set req.url = regsuball(req.url,"\?gclid=[^&]+$",""); # strips when QS = "?gclid=AAA"
set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar"
set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz"

# Static files caching
if (req.url ~ "^/(pub/)?(media|static)/") {
# Static files should not be cached by default
return (pass);

# But if you use a few locales and don't use CDN you can enable caching static files by commenting previous line (#return (pass);) and uncommenting next 3 lines
#unset req.http.Https;
#unset req.http.X-Forwarded-Proto;
#unset req.http.Cookie;

return (hash);

sub vcl_hash {
if (req.http.cookie ~ "X-Magento-Vary=") {
hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1"));

# For multi site configurations to not cache each other's content
if ( {
} else {

# To make sure http users don't see ssl warning
if (req.http.X-Forwarded-Proto) {


sub vcl_backend_response {

set beresp.grace = 3d;

if (beresp.http.content-type ~ "text") {
set beresp.do_esi = true;

if (bereq.url ~ "\.js$" || beresp.http.content-type ~ "text") {
set beresp.do_gzip = true;

if (beresp.http.X-Magento-Debug) {
set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;

# cache only successfully responses and 404s
if (beresp.status != 200 && beresp.status != 404) {
set beresp.ttl = 0s;
set beresp.uncacheable = true;
return (deliver);
} elsif (beresp.http.Cache-Control ~ "private") {
set beresp.uncacheable = true;
set beresp.ttl = 86400s;
return (deliver);

# validate if we need to cache it and prevent from setting cookie
# images, css and js are cacheable by default so we have to remove cookie also
if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
unset beresp.http.set-cookie;

# If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass
if (beresp.ttl <= 0s ||
beresp.http.Surrogate-control ~ "no-store" ||
(!beresp.http.Surrogate-Control &&
beresp.http.Cache-Control ~ "no-cache|no-store") ||
beresp.http.Vary == "*") {
# Mark as Hit-For-Pass for the next 2 minutes
set beresp.ttl = 120s;
set beresp.uncacheable = true;

return (deliver);

sub vcl_deliver {
if (resp.http.X-Magento-Debug) {
if (resp.http.x-varnish ~ " ") {
set resp.http.X-Magento-Cache-Debug = "HIT";
set resp.http.Grace = req.http.grace;
} else {
set resp.http.X-Magento-Cache-Debug = "MISS";
} else {
unset resp.http.Age;

# Not letting browser to cache non-static files.
if (resp.http.Cache-Control !~ "private" && req.url !~ "^/(pub/)?(media|static)/") {
set resp.http.Pragma = "no-cache";
set resp.http.Expires = "-1";
set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, max-age=0";

unset resp.http.X-Magento-Debug;
unset resp.http.X-Magento-Tags;
unset resp.http.X-Powered-By;
unset resp.http.Server;
unset resp.http.X-Varnish;
unset resp.http.Via;
unset resp.http.Link;

sub vcl_hit {
if (obj.ttl >= 0s) {
# Hit within TTL period
return (deliver);
if (std.healthy(req.backend_hint)) {
if (obj.ttl + 300s > 0s) {
# Hit after TTL expiration, but within grace period
set req.http.grace = "normal (healthy server)";
return (deliver);
} else {
# Hit after TTL and grace expiration
return (fetch);
} else {
# server is not healthy, retrieve from cache
set req.http.grace = "unlimited (unhealthy server)";
return (deliver);

Docker conf

Now we update the content in our file docker-compose.yml. We simply add the remaining services.

    version: '3'


image: nginx:1.16
- varnish
- 80:80
- 443:443
- ./config/nginx/includes:/etc/nginx/includes
- ./config/ssl:/etc/nginx/certs
- ./config/nginx/frontend.conf:/etc/nginx/conf.d/default.conf
- .:/var/www/html:delegated,ro


image: nginx:1.16
- php
- .:/var/www/html:delegated,ro
- ./config/nginx/includes:/etc/nginx/includes
- ./config/nginx/backend.conf:/etc/nginx/conf.d/default.conf

image: million12/varnish
- backend
- ./config/varnish/default.vcl:/etc/varnish/default.vcl
- .:/var/www/html:delegated,ro
VARNISHD_PARAMS: '-p http_resp_hdr_len=923648 -p http_resp_size=956416 -p workspace_backend=923648'

Configure the Host

The only thing you need is to register the domain on your machine. for which from the terminal we execute the following:

    echo "" | sudo tee -a /etc/hosts


First we need to generate the certificate.

In order not to make the Post boring and long, you can download or clone the files from our repository.

click on the following link

Once you have the files, the next thing is to generate the certificates in the following way. From the terminal we execute:

    cd config/ssl && ./

To install the certificate I leave a good post below:

How to install a digital certificate on a Mac


It is important to restart our containers so that the new configuration is taken into account! To do this from the terminal we execute the following:

    docker-compose down -v && docker-compose up -d

Finally, we have to tell magento which is our domain name or our base url address.

For which from the terminal we execute the following.

    bin/mage config:set web/unsecure/base_url '' --lock-env \
bin/mage config:set web/secure/base_url '' --lock-env

And that's it! We go to our browser and write the configured address. "".

If everything went well then congratulations and you installed magento in a professional, organized and structured way! See you in the next post!