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";
    }
}
up vote 7 down vote accepted

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

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.

  • 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 Sep 10 '14 at 21:50
  • 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 Feb 21 '15 at 21:44
  • 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 Mar 23 '16 at 7:16
  • @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 Apr 11 '16 at 18:59
  • @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 Apr 25 '16 at 14:46

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;
}
  • Not sure, but it might be helpful if we could see an example of your ssl.conf – thanks. – kerns Feb 12 '16 at 20:39
  • only charset isnt valid there – a deer Oct 17 '16 at 18:18

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.

  • 1
    Why not post the contents of the gist directly into your answer? This is basically a "link-only" answer, which is frowned upon. – Lindsey D Aug 18 '16 at 15:53
  • 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 Aug 18 '16 at 15:59
  • 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 Aug 18 '16 at 16:02

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.