In this article we’ll learn on how to build nginx from source with pagespeed and SPDY.
Introduction
In this article we’re going to install all the needed software to run a full featured web server on CentOS 7 using nginx with pagespeed and SPDY.
We’re also going to setup a Drupal vhost at the end of the guide so we can test our new and shiny web server.
This article is lazy mode compatible, just run the command below and you should have a fully working web server in around 5 minutes 😉
sudo bash -c "$(curl -fsSL https://gist.githubusercontent.com/fabioneves/849bbba81932aa730037/raw/7c27427ffb8bc75fa7eb7bf1dd19e284c98ce2d4/nginx-box.sh)"
But, if you want to learn something and do it by yourself, keep reading it!
We’ll install the following software:
- – nginx 1.7.12 with ngx_pagespeed and SPDY
- – MariaDB 10
- – PHP 5.6.x with PHP-FPM
- – Memcached
- – Drupal 7.x
The referenced versions in this article were the most recent at the time. If you find that there’s a more up-to-date version, you just need to replace it and it should work just fine.
Prerequisites
This guide assumes you already have a CentOS 7 system ready, it should be minimal, with no web related software installed (minimal install is best). All this commands and the script provided was tested on a RunAbove CentOS 7 cloud instance.
All the following commands in this guide, should be ran as root.
CentOS extra repositories
Before starting, let’s add EPEL and Remi repositories so we can get the most up to date versions of the software we’re gonna install and also for system update.
EPEL
wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch.rpm
rpm -Uvh epel-release-7*.rpm
Remi
wget http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
rpm -Uvh remi-release-7*.rpm
Enable Remi repository
remi=`ex /etc/yum.repos.d/remi.repo <<-EOF
/^\[remi\]
/^enabled=
s/=0/=1/
/^\[remi-php56\]
/^enabled=
s/=0/=1/
wq
EOF
System update
yum update -y
Install required packages
yum install -y gcc-c++ pcre-dev pcre-devel zlib-devel make unzip openssl-devel git libxml2-devel libxslt-devel gd-devel perl-ExtUtils-Embed GeoIP-devel libatomic_ops-devel
Build nginx from source
So now let’s build nginx from source with ngx_pagespeed and SPDY.
download ngx_pagespeed module
git clone https://github.com/pagespeed/ngx_pagespeed.git ngx_pagespeed
cd ngx_pagespeed
wget https://dl.google.com/dl/page-speed/psol/1.9.32.3.tar.gz
tar -xzvf 1.9.32.3.tar.gz
cd ..
download upload progress module
wget https://github.com/masterzen/nginx-upload-progress-module/archive/v0.9.1.tar.gz
tar zxvf v0.9.1.tar.gz
create nginx user
useradd nginx
usermod -s /sbin/nologin nginx
download nginx
wget http://nginx.org/download/nginx-1.7.12.tar.gz
tar zxvf nginx-1.7.12.tar.gz
cd nginx-1.7.12
configure
./configure --user=nginx --group=nginx --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_spdy_module --with-select_module --with-poll_module --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_spdy_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module --with-http_image_filter_module --with-http_geoip_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_stub_status_module --with-http_perl_module --with-mail --with-mail_ssl_module --with-cpp_test_module --with-cpu-opt=CPU --with-pcre --with-pcre-jit --with-md5-asm --with-sha1-asm --with-zlib-asm=CPU --with-libatomic --with-debug --with-ld-opt="-Wl,-E" --add-module=../ngx_pagespeed --add-module=../nginx-upload-progress-module-0.9.1
build
make && make install
nginx configuration
I like to have all the configuration files for nginx in blocks, meaning that I prefer to have them separated by type of configuration, so we’ll create a config file for gzip, pagespeed, core, etc. You can check more about the core nginx configuration in the official website.
main nginx config
cat <<EOF > /etc/nginx/nginx.conf
user nginx;
worker_processes 1;
pid /run/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
use epoll;
}
http {
include mime.types;
default_type application/octet-stream;
# configuration files
include /etc/nginx/conf.d/*.conf;
# vhosts
include /etc/nginx/sites-enabled/*;
}
EOF
fastcgi params config file
cat <<EOF > /etc/nginx/fastcgi_params
fastcgi_param QUERY_STRING \$query_string;
fastcgi_param REQUEST_METHOD \$request_method;
fastcgi_param CONTENT_TYPE \$content_type;
fastcgi_param CONTENT_LENGTH \$content_length;
fastcgi_param SCRIPT_FILENAME \$request_filename;
fastcgi_param SCRIPT_NAME \$fastcgi_script_name;
fastcgi_param REQUEST_URI \$request_uri;
fastcgi_param DOCUMENT_URI \$document_uri;
fastcgi_param DOCUMENT_ROOT \$document_root;
fastcgi_param SERVER_PROTOCOL \$server_protocol;
fastcgi_param HTTPS \$https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/\$nginx_version;
fastcgi_param REMOTE_ADDR \$remote_addr;
fastcgi_param REMOTE_PORT \$remote_port;
fastcgi_param SERVER_ADDR \$server_addr;
fastcgi_param SERVER_PORT \$server_port;
fastcgi_param SERVER_NAME \$server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
EOF
create conf.d folder for our config files
mkdir /etc/nginx/conf.d
nginx core config
cat <<EOF > /etc/nginx/conf.d/core.conf
## sendfile and tcp_nopush
## - Ensures that the packets are full before sending to the client.
sendfile on;
tcp_nopush on;
## tcp_nodelay
## - Forces the socket to send the data (saving up to 0.2 seconds per file (nagle's algorithm)).
tcp_nodelay on;
## server_tokens
## - Enables or disables emitting nginx version in error messages and in the 'Server' response header field.
server_tokens off;
## client_max_body_size
## - Sets the maximum allowed size of the client request body, specified in the 'Content-Length' request header field.
## If the size in a request exceeds the configured value, the 413 (Request Entity Too Large) error is returned to the client.
client_max_body_size 256m;
## keepalive_timeout
## - Sets a timeout during which a keep-alive client connection will stay open on the server side (default 75s).
keepalive_timeout 30;
## client_header_timeout
## - Defines a timeout for reading client request header.
client_header_timeout 10;
## client_body_timeout
## - Defines a timeout for reading client request body.
client_body_timeout 10;
## send_timeout
## - Sets a timeout for transmitting a response to the client.
send_timeout 10;
EOF
nginx gzip config
cat <<EOF > /etc/nginx/conf.d/gzip.conf
gzip on;
gzip_disable "msie6";
gzip_http_version 1.1;
gzip_vary on;
gzip_comp_level 6;
gzip_min_length 0;
gzip_proxied any;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
EOF
nginx pagespeed config
There’s a lot to talk about pagespeed configuration. Every pagespeed config should be adapted to your use case/website, this config should be useful as a starting point. You can check more about pagespeed filters and config options here.
cat <<EOF > /etc/nginx/conf.d/pagespeed.conf
pagespeed on;
pagespeed FileCachePath /var/cache/ngx_pagespeed_cache;
pagespeed FileCacheSizeKb 102400;
pagespeed FileCacheCleanIntervalMs 3600000;
pagespeed FileCacheInodeLimit 500000;
pagespeed LRUCacheKbPerProcess 8192;
pagespeed LRUCacheByteLimit 16384;
pagespeed MemcachedServers "127.0.0.1:11211";
pagespeed RewriteLevel PassThrough;
pagespeed EnableFilters remove_comments,collapse_whitespace,rewrite_images,resize_images,resize_rendered_image_dimensions,prioritize_critical_css,insert_dns_prefetch,combine_css,rewrite_css,combine_javascript,rewrite_javascript;
pagespeed RespectVary on;
pagespeed CriticalImagesBeaconEnabled false;
pagespeed StatisticsPath /ngx_pagespeed_statistics;
pagespeed GlobalStatisticsPath /ngx_pagespeed_global_statistics;
pagespeed MessagesPath /ngx_pagespeed_message;
pagespeed ConsolePath /pagespeed_console;
pagespeed AdminPath /pagespeed_admin;
pagespeed GlobalAdminPath /pagespeed_global_admin;
pagespeed MessageBufferSize 200000;
pagespeed Statistics on;
pagespeed StatisticsLogging on;
pagespeed LogDir /var/log/pagespeed;
pagespeed StatisticsLoggingIntervalMs 60000;
pagespeed StatisticsLoggingMaxFileSizeKb 1024;
EOF
Special attention for the ngx_pagespeed admin pages, you should block them from the public in your vhost configuration. You can read more about the pagespeed admin pages here.
Here’s an example on how to block the statistics page:
location /ngx_pagespeed_statistics { allow 127.0.0.1; deny all; }
systemd
CentOS 7 uses the new systemd, so let’s create a service for our newly compiled nginx. Run the following command:
cat >> /usr/lib/systemd/system/nginx.service << NGINX_SERVICE
[Unit]
Description=The nginx HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
NGINX_SERVICE
Enable the service
systemctl enable nginx
virtual host config directories
Create the directories for our virtual host configurations.
mkdir /etc/nginx/sites-available /etc/nginx/sites-enabled
Ok, we’ve finished the nginx build and config.
Install PHP and memcached
Just run the following yum command to install PHP with zend opcache, together and some other PHP extensions, feel free to add as many as you want/need. Memcached will also be installed.
yum install -y php php-gd php-pdo php-fpm php-pecl-zendopcache php-mbstring php-mysql php-pecl-uploadprogress memcached
We now have PHP with PHP-FPM installed, let’s create our global php-fpm socket to run PHP in our vhosts.
cat <<EOF > /etc/php-fpm.d/global-pool.conf
[global-pool]
user = nginx group = nginx listen = /var/run/php-fpm/php-fpm-global.sock listen.owner = nginx listen.group = nginx listen.mode = 0660 pm = dynamic pm.start_servers = 1 pm.max_children = 5 pm.min_spare_servers = 1 pm.max_spare_servers = 5 EOF
We should change some of the PHP default settings such as memory_limit, upload size, etc. Here’s a command that will apply the following settings to /etc/php.ini config file:
- date time zone to Europe/Lisbon
- memory limit to 256M
- upload size limit to 2GB
- max input and exec time to 300 seconds
- max input vars to 3000
php_config=`ex /etc/php.ini <<-EOF
/^post_max_size
s/= 8M/= 2048M/
/^upload_max_filesize
s/= 2M/= 2048M/
/^; max_input_vars
s/= 1000/= 1000\rmax_input_vars = 3000/
/^memory_limit
s/= 128M/= 256M/
/^max_input_time
s/= 60/= 300/
/^max_execution_time
s/= 30/= 300/
/^\[Date\]
/^;date.timezone
s/=/=\rdate.timezone = Europe\/Lisbon/
wq
EOF`
As with all the config files, you’re free to choose the values as you prefer/like, this is by no means a “mandatory” config for running your web server. You should always adapt the configuration to the purpose of the server and it’s websites.
zend opcache
The zend opcache configuration file is found here: /etc/php-zts.d/10-opcache.ini and it’s enabled by default. If you want to disable it, just change the opcache.enable=1 value to 0.
Install MariaDB 10
MariaDB is designed as a drop-in replacement of MySQL(R) with more features, new storage engines, fewer bugs, and better performance.
add MariaDB official centos repo
cat >> /etc/yum.repos.d/mariadb.repo << MARIADB
# MariaDB 10.0 CentOS repository list - created 2015-04-15 11:21 UTC
# http://mariadb.org/mariadb/repositories/
[mariadb]
name = MariaDB baseurl = http://yum.mariadb.org/10.0/centos7-amd64 gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB gpgcheck=1 MARIADB
install MariaDB
yum install -y MariaDB-server MariaDB-client
start MariaDB server
service mysql start
Now we have to secure your MariaDB installation with the command (DON’T skip this step):
mysql_secure_installation
Enable the services
Ok, right now we have nginx, php, memcached and mariadb installed. We should enable all our services, so they’re started on the server boot.
systemctl disable httpd systemctl enable nginx systemctl enable php-fpmsystemctl enable memcachedservice restart nginx service restart php-fpm service restart memcached
You might have noticed that we disabled httpd service, yep that’s Apache. When we install php through yum, httpd is a dependency so it’s installed together with php, as we’re not gonna use apache, let’s just disable and forget it exists 😉
MariaDB uses the old init.d scripts, when we installed it, it was set to start on server boot, so we don’t need to do anything about that. You can check this by typing chkconfig on console.
So now, all we have left to do is to install drupal,you can skip this step if you want as you already have your web server running and ready to roll. Anyway, even if you don’t want to installdrupal you should at least read the next section as it explains on how to setup a vhost.
Install drupal
First let’s create a simple vhost configuration for our drupal website including SPDY configuration. As I said, this is a simple configuration, you can customise it much more, I recommend you to check perusio’s github repo about drupal with nginx.
First let’s generate a self-signed certificate so we can test/use the vhost using SPDY.
mkdir /etc/nginx/ssl
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=localhost" -keyout /etc/nginx/ssl/localhost.key -out /etc/nginx/ssl/localhost.crt
vhost configuration
You should replace localhost with your domain name.
cat <<EOF > /etc/nginx/sites-available/drupal.conf
server {
client_max_body_size 64M;
listen 80;
server_name localhost;
root /home/nginx/drupal;
index 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 /var/log/nginx/drupal-access.log;
error_log /var/log/nginx/drupal-error.log;
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Pragma public;
add_header Cache-Control "public";
try_files \$uri =404;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php-fpm/php-fpm-global.sock;
fastcgi_index index.php;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}
server {
client_max_body_size 64M;
listen 443 ssl spdy;
server_name localhost;
root /home/nginx/drupal;
ssl_certificate /etc/nginx/ssl/localhost.crt;
ssl_certificate_key /etc/nginx/ssl/localhost.key;
index 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 /var/log/nginx/drupal-access.log;
error_log /var/log/nginx/drupal-error.log;
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Pragma public;
add_header Cache-Control "public";
try_files \$uri =404;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php-fpm/php-fpm-global.sock;
fastcgi_index index.php;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}
EOF
Once you have the vhost ready, add this extra line:
pagespeed LoadFromFile "http://<SERVER_IP or DOMAIN>/" "/home/nginx/drupal/";
This will make pagespeed work much better with your static content, sometimes it can’t find it and fails to apply the filters as it should.
Don’t forget to link your vhost config, everytime you create a new one.
ln -s /etc/nginx/sites-available/drupal.conf /etc/nginx/sites-enabled/drupal.conf
install drush
Drush is an awesome cli and it’s essential for any drupal developer. We’re going to install drupal with drush.
pear channel-discover pear.drush.org
pear install drush/drush
create database to install drupal
We need to create a database for our drupal installation. Replace <MYSQL_ROOT_PASSWORD> for the root password you’ve set, when you secured MariaDB server installation and <DRUPAL_DB_PASSWORD> with the password you want for your new drupal database (yes, also replace <>).
mysql -uroot -p<MYSQL_ROOT_PASSWORD> -e "create database drupal"
mysql -uroot -p<MYSQL_ROOT_PASSWORD> -e "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES ON drupal.* TO 'drupal'@'localhost' IDENTIFIED BY '<DRUPAL_DB_PASSWORD>'"
install drupal with drush
We’re going to setup our drupal install in /home/nginx.You can choose to place your vhosts anywhere you like as long as nginx have access to it. Replace <DRUPAL_DB_PASSWORD> with your drupal db password.
cd /home/nginx
drush dl drupal --drupal-project-rename=drupal
cd drupal
drush site-install standard --db-url="mysql://drupal:<DRUPAL_DB_PASSWORD>@localhost/drupal" --site-name=Drupal -y
drush dis overlay -y
chown -R nginx:nginx /home/nginx/drupal
If everything went well, you should have drupal installed without that horrendous overlay module. The user and password should be printed to the console.
To access your website just type http://<SERVER_IP> in your browser. Use https for SPDY greatness.
Conclusion
Ok, that’s it, hope you learned something from my guide. You should have a pretty solid base to play with pagespeed and SPDY by now. If you have any questions or feedback feel free to post in the comments.
Take care.
Leave your reply