How did I setup a wordpress hosting in AWS using nginx+php-fpm

Well, for a long time I have been looking for the perfect WordPress hosting setup. I end up using the combination of nginx, php-fpm and memcached. The other option is using apache and php while the difference is the way these two web server handle request and use php. As far as I understand, nginx has a more simple way of dealing with php using php-fpm and the way it handle modules and caching boosts the performance.

So even though the nginx configuration is like a nightmare I start giving it a try and after so many challenges I end up making it working! Obviously, the apache configuration is much simpler but if you need to satisfy millions of users then nginx can be a better option.

In my experience I used an EC2 t2.medium instance (just for 5 hosts) and Amazon Linux AMI as OS. So now we are going to install the services and modules before starting the configurations.

First we install the followings and create a cache directory for nginx fast-cgi:

yum install nginx
yum install php56-fpm
mkdir -p /var/cache/nginx/
yum install php56-mysqlnd #if needed
yum install memcached #if needed

(I assumed your MySQL is hosted in another server.)

And then we create the root directory for websites with a simple default html file. The nginx process is the owner along appropriate permissions cause no one supposed to change it except us!

mkdir -p /var/nginx/sites/default
echo 'You should not be here!' > /var/nginx/sites/default/index.html
chown -R nginx:nginx /var/nginx/sites/default
chmod -R 701 /var/nginx/sites/default 

and then making the website root directory with right owner and permissions (the ftp-user group contains all users for that group and all of them only are able to access to this directory).

mkdir -p /var/nginx/sites/
chown -R nginx:abc-ftp /var/nginx/sites/
chmod -R 711 /var/nginx/sites/ 

Appendix 1 and 3 should be available in proper directory and appendix 2 template should be used for adding additional websites to host.

And finally test the configurations: service nginx configtest

Now it’s time to start the engine! service nginx start; service php-fpm start;

now copy your wordpress source in /var/nginx/sites/ folder and you should be able to browse your website(s) and just use the wizard to setup database connection.

Once you entered the wordpress admin panel you can install W2 Total Cache plugins which can be configured to use memcached for a boost in performance. After all the performance is mostly about caching so maybe in another post I explain more about wordpress caching options.

Please remember to change the user and group in /etc/php-fpm.d/www.conf file:
user = nginx
group = nginx

Appendix 1: /etc/nginx/nginx.conf

user  nginx;
worker_processes  1;
#error_log  /var/log/nginx/error.log;
pid        /var/run/;
events {
    worker_connections  1024;
    use epoll;
    multi_accept on;
http {
    include       /etc/nginx/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  /var/log/nginx/access.log;
    #error_log /var/log/nginx/error.log;
 ## Cache
    open_file_cache max=1000 inactive=24h;
    open_file_cache_valid 24h;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;
 ## Timeouts
    client_header_timeout  3m;
    client_body_timeout    3m;
    send_timeout           3m;
    keepalive_timeout      30s;
 ## Size limits
    client_max_body_size   15m;
    client_header_buffer_size   32k;
    client_body_buffer_size  128k;
    large_client_header_buffers 64 8k;
 ## General
    types_hash_max_size 2048;
    server_names_hash_bucket_size 64;
    ignore_invalid_headers   on;
    keepalive_requests       100;
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    recursive_error_pages    on;
    reset_timedout_connection on;
    sendfile       on;
    tcp_nopush     on;
    tcp_nodelay    on;
    server_tokens  off;
    server_name_in_redirect off;
 ## Compression
    gzip  on;
    gzip_static on;
    gzip_comp_level 6;
    gzip_disable "msie6";
    gzip_buffers   32 8k;
    gzip_vary on;
    gzip_proxied any;
    gzip_min_length 0;
    gzip_http_version 1.0;
    gzip_types text/css text/javascript text/xml text/plain application/javascript application/x-javascript application/json application/xml application/rss+xml;
    output_buffers   10 128k;
    postpone_output  1500;
    fastcgi_cache_path /var/cache/nginx/fastcgi_temp levels=1:2 keys_zone=CZONE:15m inactive=60m;
    fastcgi_cache_key "$scheme$request_method$host$request_uri";
    fastcgi_cache_use_stale error timeout invalid_header http_500;
 ## The hosts
    include /etc/nginx/site.d/*.conf;
    server {
        listen 80 default_server;
        index index.html;
        server_name localhost;
        root /var/nginx/sites/default;
        access_log /var/log/nginx/default.access.log;
        error_log /var/log/nginx/default.error.log;

Appendix 2: /etc/nginx/site.d/

server {
    root /var/nginx/sites/;

    access_log /var/log/nginx/;
    error_log /var/log/nginx/;
    include /etc/nginx/site.d/*.wsconf;

    location ~ ^/(just|sample|rewrite)/.+/?$ {
        rewrite ^/(.+)/(.+)/?$ /$1/?p=$2?;
        set $rewritten_uri $uri;

Appendix 3: /etc/nginx/site.d/universal.wsconf

    listen 80;
    index index.php index.html index.htm;
    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
        #proxy_read_timeout 300;
    location ~* /(?:uploads|files)/.*\.php$ {
        deny all;

    rewrite ^/sitemap_index\.xml$ /index.php?sitemap=1 last;
    rewrite ^/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;
    if (!-e $request_filename) {
        rewrite /wp-admin$ $scheme://$host$uri/ permanent;
        rewrite ^/[_0-9a-zA-Z-]+(/wp-.*) $1 last;
        rewrite ^/[_0-9a-zA-Z-]+(/.*\.php)$ $1 last;

   location = /favicon.ico {
        log_not_found off;
        access_log off;
    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    location = /50x.html {
        root /usr/share/nginx/html;
    location ~ /\. {
        deny all;
    location ~* ^.+\.(js|css|swf|xml|txt|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
        access_log off; log_not_found off; expires 30d;

 ## No caching for logged in users
    set $no_cache 0;
    if ($request_method = POST) {
        set $no_cache 1;
    if ($query_string != "") {
        set $no_cache 1;
    if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
        set $no_cache 1;
    if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
        set $no_cache 1;

## PHP settings
    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_intercept_errors on;
        fastcgi_ignore_client_abort off;
        fastcgi_connect_timeout 60;
        fastcgi_send_timeout 90;
        fastcgi_read_timeout 10m;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 4 256k;
        fastcgi_busy_buffers_size 256k;
        fastcgi_temp_file_write_size 256k;
        fastcgi_cache_bypass $no_cache;
        fastcgi_no_cache $no_cache;
        fastcgi_cache   CZONE;
        fastcgi_cache_valid   200 302  1h;
        fastcgi_cache_valid   301 1h;
        fastcgi_cache_valid   any 1h;
        fastcgi_cache_min_uses  2;
 ## Security
    location ~* /\.ht {
        deny            all;
        access_log      off;
        log_not_found   off;


Solving the blank screen issue for PHP files:
Add “fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;” to /etc/nginx/fastcgi_param


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s