Vhost Configuration To Host Magento On Nginx

back to tech articles
Magento CE 1.8.0.0 Alpha, Nginx 1.4.2, PHP-FPM 5.5.4

I had to pluck up the courage to dump Apache in favour of Nginx as my Magento server. By the way, Nginx is pronounced ‘Engine X’.

Don’t get me wrong, I love Apache. The guys behind it are pioneers and the Apache server currently delivers about 3 quarters of the net. However, the facts are when considering a server for a high-traffic environment, Nginx is a far more scalable platform (I tested against Apache 2.4.2). Add to that it’s reverse-proxy ability (caching) and you have a real winner.

Problem is, it’s daunting for the new user to configure it, especially for a platform like Magento. Remember, no Apache means no .htaccess, gasp!

So after modifying a few stock configs for my own Magento install and getting an education over at Nginx, here’s my own findings for a stable and fast ‘vhost’.

Be warned though; the Nginx docs are not human-friendly, unless you can read and write in Sindarin.

The Setup

So here’s my pickle, and the reason most other stock configurations won’t work for what I have going on.

  • 2 servers, 1 serves the codebase, another the db.
  • 1 codebase.
  • 1 IP address.
  • 2 domains (SSL considerations here), offering a website each.
  • 4 stores for each domain.
  • Several off-the-shelf modules.
  • A WordPress blog in /blog sub-directory.

Here’s my vhost file, and it caters for all these considerations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
## domain.com
server {
    server_name www.domain.com;
    return 301 $scheme://domain.com$request_uri;
}

server {
    listen 80;
    listen 443 ssl;
    ssl_certificate /etc/ssl/certs/domain_com_public.crt;
    ssl_certificate_key /etc/ssl/certs/domain_com_private.key;
    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;

    server_name domain.com;
    root /var/public_html/domain.com;

    ## maintenance mode ##
    ######################

#    set $maintenance on;
#    if ($remote_addr ~ (8.8.8.8|8.8.4.4)) {
#        set $maintenance off;
#    }
#    if ($uri ~ ^/(index.php/)?(paypal)/(.*)$ ) {
#        set $maintenance off;
#    }
#    if ($maintenance = on) {
#        return 503;
#    }
#    location /maintenance {}
#    error_page 503 @maintenance;
#    location @maintenance {
#        root /var/public_html/domain.com/maintenance;
#        rewrite ^(.*)$ /index.html break;
#    }

    ##########################
    ## end maintenance mode ##

    location = /favicon.ico {
        try_files $uri =204;
    }

    location ~* \.(jpe?g|gif|css|png|js|ico|pdf|zip|tar|t?gz|mp3|wav|swf)$ {
        expires max;
    }

    location / {
        index index.html index.php;
        try_files $uri $uri/ @handler;
        rewrite ^/my-url.html /my-url/ permanent;
        rewrite ^/my-url.html http://domain.co.uk/ permanent;
        expires 7d;
    }

    location /blog {
        rewrite ^/blog.html /blog/ permanent;
        index index.php index.html index.htm;
        try_files $uri $uri/ /blog/index.php;
    }

    location ~ ^/(app|includes|media/downloadable|pkginfo|report/config.xml|var)/ { deny all; }

    location ~ ^/(info.php|var/export/)/ {
        auth_basic "Restricted Access";
        auth_basic_user_file /etc/nginx/htpasswd;
    }

    location @handler {
        rewrite / /index.php;
    }

    location ~ .php/ {
        rewrite ^(.*.php)/ $1 last;
    }

    location ~ .php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param MAGE_RUN_TYPE website;
        fastcgi_param MAGE_RUN_CODE base;
        include fastcgi_params;
    }
}

I have a seperate vhosts file like this for each TLD. These reside in the /etc/nginx/sites/ folder. Remember to change the domain name settings for each domain name or suffix!

Multi-Site Considerations

Some SSL certificates will require a seperate IP address for each top level domain name. This is not necessarily required by the server as well. As you can see from this article; I’m serving two domains (each with it’s own SSL certificate) from one codebase using a single IP address. No SNI required here.

You can achieve this by defining a seperate vhost for each domain, as I’ve done. For example, one for your .com site, and one for your .co.uk site. Just change the relevant details and point them to the same physical location on the server (eg. /var/public_html/domain/site/).

The key lines are these:

1
2
fastcgi_param MAGE_RUN_TYPE website;
fastcgi_param MAGE_RUN_CODE base_com;

That tells Nginx what to pass to the application as a parameter. Job done. Obviously, these could be store definitions as well:

1
2
fastcgi_param MAGE_RUN_TYPE store;
fastcgi_param MAGE_RUN_CODE english_uk;

Magento grabs that parameter and serves the requested website or store. And it happens at lightning speed.

Is It Worthwhile

It’s true that it can be a pain using Nginx for the first while – new errors crop up and your immediate thought would be Magento’s caching engine, indexing service or something similar, only to find it was the server all along.

But I’ve given you what I consider to be a stable configuration here, so half the problem there is already solved!

Regardless, I do think it is worthwhile switching to Nginx for high-traffic sites. Even coupled with Varnish, Apache could not compare in my tests for concurrent users on site. Nginx is rumoured to be around 100 times faster, but in my real-world tests I found it was about 3 – 5 times faster. But, to quantify that, it’s the difference between serving 100 requests or 500 requests over the same time period.

Plus it performs a full service restart in about 200ms. Awesome!

P.S: check your configurations with nginx -t before restarting the service 😉

Happy hosting!