What is the recommended Nginx configuration for Craft?

16

18

I'm looking for the recommended way of how to configure Nginx server to run Craft.

Currently, I use the following configuration, it works, but I don't know if it could be better:

server {
    listen 80;
    server_name example.com;
    root /home/vagrant/craft/public;

    index index.html index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    access_log off;
    error_log  /var/log/nginx/example.com-error.log error;

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
    }

    location ~* (?:^|/)\. {
        deny all;
    }

    location ~* (?:\.(?:bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)|~)$ {
        deny all;
    }

    location ~* \.(?:manifest|appcache|html?|xml|json)$ {
        try_files $uri /index.php?$query_string;
        expires -1;
        access_log /var/log/nginx/example.com-access.log;
    }

    location ~* \.(?:rss|atom)$ {
        try_files $uri /index.php?$query_string;
        expires 1h;
        add_header Cache-Control "public";
    }

    location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
        try_files $uri /index.php?$query_string;
        expires 1M;
        add_header Cache-Control "public";
    }

    location ~* \.(?:css|js)$ {
        try_files $uri /index.php?$query_string;
        expires 1y;
        add_header Cache-Control "public";
    }

    location ~* \.(?:ttf|ttc|otf|eot|woff)$ {
        try_files $uri /index.php?$query_string;
        expires 1M;
        add_header Cache-Control "public";
    }
}

dralshehri

Posted 2014-07-11T20:50:04.950

Reputation: 410

Answers

6

This Nginx-Craft configuration is one I've been using in production for some time, and it implements many best-practices that you might want to be using:

https://github.com/nystudio107/nginx-craft

andrew.welch

Posted 2014-07-11T20:50:04.950

Reputation: 5 136

10

Definitely not an nginx expert, but this has been working well for anyone that's tried it so far:

server {
  listen 8080 default;
  charset utf8;
  server_name example.com;
  root /path/to/example.com/public;
  index index.php index.html;

  # Logs
  error_log /usr/local/etc/nginx/logs/example.com.error.log debug;
  access_log /usr/local/etc/nginx/logs/example.com.access.log;

  location ~ \.php$ {
    include fastcgi_params;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  }

  location ~ ^(.*)$ {
      try_files $uri $uri/ /index.php?p=$uri&$args;
  }

  location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
    expires max;
    add_header Pragma public;
    add_header Cache-Control "public, must-revalidate, proxy-revalidate";
  }

  location = /robots.txt  { access_log off; log_not_found off; }
  location = /favicon.ico { access_log off; log_not_found off; }
  location ~ /\. { access_log off; log_not_found off; deny all; }
}

Note that it's making a few assumptions about how you've setup php-fpm and what ports things are listening on, so those may need to be adjusted.

Brad Bell

Posted 2014-07-11T20:50:04.950

Reputation: 53 205

Please don't do this. Adding try_files inside of that location block means that it's going to fire up php, Yii, and Craft when serving any of those matching files, both on the backend and on the front-end. This is going to be very slow and inefficient way to serve static resources. Instead make a location block for the /admin url to route only AdminCP requests through index.php – andrew.welch – 2016-03-23T07:16:18.320

@khalwat: not true. try_files actually makes sure the file doesn't already exist on the server first. In fact, most nginx configs for / start with try_files. Your suggestion would work as well but it's going to be the same outcome: internally since the file doesn't exist, nginx is going eventually route back to the php location block. – RitterKnight – 2016-04-11T18:59:48.983

@RitterKnight I thought so too... however that isn't what ended up happening. It may depend on your exact Nginx config, but in the case I mentioned, the requests for static resources were indeed being routed through index.php – andrew.welch – 2016-04-25T14:46:47.533

@khawlat: Yes try reworking your config. It's probably a case of nginx not serving out of the location block you think that it is. try_files always looks for static files first then falls back. On the admin side, all requests DO actually get routed back to PHP. That's just how Craft 2 works since those static resources live behind the document root. I haven't looked at Craft's code but there's probably a way to alias them so nginx serves them directly. Craft 3 is going to change that... – RitterKnight – 2016-04-25T15:18:33.333

Any idea how you would add cross-domain headers? When I add

location ~* \.(?:ttf|ttc|otf|eot|woff|woff2)$ { add_header "Access-Control-Allow-Origin" "*"; } I get 404s on the fonts in the admin. – Trevor Davis – 2014-09-10T21:50:57.177

1@TrevorDavis you need to add try_files directive i.e. location ~* \.(?:ttf|ttc|otf|eot|woff|woff2)$ { try_files $uri /index.php?p=$uri&$args; add_header "Access-Control-Allow-Origin" "*"; } – dralshehri – 2015-02-21T21:44:00.840

3

craftcms.conf

root /srv/http/current-release/src/public;

index index.php index.html index.htm;

charset utf-8;

# Force the latest IE version
add_header "X-UA-Compatible" "IE=Edge";

error_page 404 /index.php;

location / {
    # try_files $uri $uri/ /index.php?$query_string;
    try_files $uri $uri/ @rewrites;
}

location @rewrites {
    rewrite ^(.*) /index.php?p=$1 last;
}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

    # With php5-cgi alone:
    # fastcgi_pass 127.0.0.1:9000;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
}

# Expire rules for static content

# No default expire rule. This config mirrors that of apache as outlined in the
# html5-boilerplate .htaccess file. However, nginx applies rules by location,
# the apache rules are defined by type. A consequence of this difference is that
# if you use no file extension in the url and serve html, with apache you get an
# expire time of 0s, with nginx you'd get an expire header of one month in the
# future (if the default expire rule is 1 month). Therefore, do not use a
# default expire rule with nginx unless your site is completely static

# cache.appcache, your document html and data
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
    try_files $uri /index.php?$query_string;
    expires -1;
}

# Feed
location ~* \.(?:rss|atom)$ {
    try_files $uri /index.php?$query_string;
    expires 1h;
    add_header Cache-Control "public";
}

# Media: images, icons, video, audio, HTC
location ~* \.(?:jpg|jpeg|gif|png|pdf|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
    try_files $uri /index.php?$query_string;
    expires 1M;
    access_log off;
    add_header Cache-Control "public";
}

# CSS and Javascript
location ~* \.(?:css|js)$ {
    try_files $uri /index.php?$query_string;
    expires 1y;
    access_log off;
    add_header Cache-Control "public";
}

# Favicons
location = /favicon.ico {
    access_log off;
    log_not_found off;
}
# Robots.txt
location = /robots.txt {
    access_log off;
    log_not_found off;
}

# Prevent clients from accessing hidden files (starting with a dot)
# This is particularly important if you store .htpasswd files in the site hierarchy
location ~* (?:^|/)\. {
    deny all;
}
# Prevent clients from accessing to backup/config/source files
location ~* (?:\.(?:bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)|~)$ {
    deny all;
}

sites-available.conf

# http://example.com
# http://www.example.com
# This server redirects users to the authoritive non-WWW https site.
server {
    # don't forget to tell on which port this server listens
    #listen [::]:80;
    listen 80;
    server_name example.com www.example.com;
    # and redirect to the non-www host (declared below)
    return 301 https://example.com$request_uri;
}

# https://www.example.com
# This server redirects users to the secure non-WWW https site.
server {
    listen 443 ssl spdy;
    # listen on the www host
    server_name www.example.com;
    include ssl.conf;
    # and redirect to the non-www host (declared below)
    return 301 https://example.com$request_uri;
}

# http://<IP address>
# Allows HTTP direct to the IP address of the EC2 node
server {
    # don't forget to tell on which port this server listens
    listen 80 default_server;
    # listen on the domain name only
    server_name example.com;
    include craftcms.conf;
}

# https://example.com
# The BE ALL END ALL OF NGINX CONFIGURATIONS AT example.com
server {
    # don't forget to tell on which port this server listens
    listen 443 default_server ssl spdy;
    # listen on the domain name only
    server_name naralogics.com;
    include ssl.conf;
    include craftcms.conf;
}

0xADADA

Posted 2014-07-11T20:50:04.950

Reputation: 31

Not sure, but it might be helpful if we could see an example of your ssl.conf – thanks. – kerns – 2016-02-12T20:39:36.300

only charset isnt valid there – a deer – 2016-10-17T18:18:06.930

0

I see this is 2 years old post but just wanted to share my findings. So, if anyone comes for a solution next time, he will be able to find a better solution. Please use this gist created by me for details configuration.

Thnx.

William Francis Gomes

Posted 2014-07-11T20:50:04.950

Reputation: 101

1Why not post the contents of the gist directly into your answer? This is basically a "link-only" answer, which is frowned upon. – Lindsey D – 2016-08-18T15:53:41.377

Well as you can see, whole code will be much bigger and non-friendly. Thats why thought maybe link will serve better than whole code. – William Francis Gomes – 2016-08-18T15:59:25.997

It may be a large answer, but it would be a comprehensive (and better) answer. As it currently stands, if you hypothetically removed the link, this isn't an answer at all. (Thus it's a "link-only" answer.) – Lindsey D – 2016-08-18T16:02:02.900