Skip to content

Instantly share code, notes, and snippets.

@dakcarto
Created March 10, 2012 03:28
Show Gist options
  • Save dakcarto/2009943 to your computer and use it in GitHub Desktop.
Save dakcarto/2009943 to your computer and use it in GitHub Desktop.
Nginx 1.x Config for ExpressionEngine 2.x, with PATH_INFO and Adaptable HTTPS Support
Nginx 1.x Config for ExpressionEngine 2.x, with PATH_INFO and Adaptable HTTPS
=============================================================================
Before using Nginx configs, do the following with your EE install
-----------------------------------------------------------------
1) Remove index.php from URL. See step #2, but ignore Apache steps, of
http://expressionengine.com/user_guide/general/remove_index.php.html
2) Remove all scheme://domain.tld references from config URLs in control panel.
Set these to absolute URLs, e.g. http://ww.mydomain.com/images/avatars/ -> /images/avatars/
Places to look for URL configs to edit in control panel:
general config
channel prefs (each)
upload destinations prefs (each)
membership prefs
captchas
emoticons
(there may be others)
I have no idea why EE was designed to have the scheme://domain.tld hardcoded into configs this way.
Short of editing EE functionality, you will need to remove generated scheme://domain.tld.
Do this each time you create new items that have URLs in their individual prefs, like channels and upload destinations.
3) For auto http->https and https->http support edit /system/expressionengine/config/config.php:
Set env variable for EE to maintain http/https request state in generated URLs
$config['base_url'] = $_SERVER["scheme_url"];
$config['site_url'] = $_SERVER["scheme_url"]; <-- add this line, below above line
NOTE, after this edit, the following settings will be auto-updated and should not be manually edited via the admin control panel:
$config['site_url'] (in Config File Editor)
'URL to the root directory of your site' (in General Configuration)
For the Nginx config setup on the server (Ubuntu 10.04 noted here)
------------------------------------------------------------------
(edit the file names to suit, though make sure the other configs are updated with the new names of included files)
/etc/nginx/sites-available/my-domain-com <-- can be mixed in with other server blocks
I make a /etc/nginx/incl.d directory, since putting configs in conf.d loads them for all server blocks.
/etc/nginx/incl.d/ee_support_my-domain-com <-- created for each server block domain
/etc/nginx/incl.d/ee_fastcgi_params <-- not domain-specific
On my setup, results using ApacheBench, Version 2.3
---------------------------------------------------
(using a default EE 2.4 install, with the demo site)
With command: ab -n500 -c20 'http://my.domain.tld/'
Yields the following, WITHOUT any caching by EE, Nginx or CDN (CloudFare.com, in this setup):
Requests per second: 23.99 [#/sec] (mean)
Time per request: 833.836 [ms] (mean)
Time per request: 41.692 [ms] (mean, across all concurrent requests)
Transfer rate: 273.06 [Kbytes/sec] received
Regards,
Larry S
# support for PATH_INFO, e.g. /index.php/segment/segment
fastcgi_split_path_info ^(.+\.php)(/?.+)$;
# $config['uri_protocol'] = 'PATH_INFO'; can be used
# PATH_INFO should show 'yes' in ee_wizard results
# test for actual php file, see: http://wiki.nginx.org/Pitfalls
if ($fastcgi_script_name ~* \..*/.*\.php$) {
return 404;
}
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
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 REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx;
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;
fastcgi_param HTTPS $server_https;
## For HTTPS support (in addition to 'ee_support_[subdomain]' config file) ##
# set env variable for ee to maintian http/https request state in generated URLs
# for use by $base_url and $site_url, edit /system/expressionengine/config/config.php:
# $config['base_url'] = $_SERVER["scheme_url"];
# $config['site_url'] = $_SERVER["scheme_url"]; <-- add this line, below above line
fastcgi_param scheme_url "$scheme://$host/";
fastcgi_index index.php;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
### Support for ExpressionEngine 2.x ###
### Edit lines under comments with [configure] ###
## Rewrite rules to incorporate HTTPS connections ##
## (in addition to 'ee_fastcgi_params' config file) ##
# the following lists all boolean tests before concatenated tests, which is less
# efficient than mixing in the concatenated tests after cumulative boolean tests,
# but more easily read (reorganize for speed increase on heavy traffic sites)
# for debugging add 'debug' to error log config in server block
# (check error log for very verbose, albeit confusing, rewrite logging)
## boolean tests for building concatenated tests ##
# request via GET
set $get_req "0";
if ($request_method ~ "GET") {
set $get_req "1";
}
# request via POST
set $post_req "0";
if ($request_method ~ "POST") {
set $post_req "1";
}
# request via https [configure to your HTTPS port]
set $secure_req "0";
if ($server_port = 443) {
set $secure_req "1";
}
# request is for root '/' with no query
set $root_req_only "0";
if ($request_uri ~ "^/$") {
set $root_req_only "1";
}
# request has query string
set $query_req "0";
if ($is_args = "?") {
set $query_req "1";
}
# request is for 'static' resource [configure]
# NOTE: these are resources to be used by needs-to-be-secure areas
set $static_resource "0";
if ($uri ~* "\.(?:css|js|gif|jpe?g|png)$") {
set $static_resource "1";
}
# request contains index.php
set $indexphp_req "0";
if ($request_uri ~* "index\.php") {
set $indexphp_req "1";
}
# request contains needs-to-be-secure area [configure]
# NOTE: 'system' directories need not be included, as they should be blocked (see bleow)
set $secure_area "0";
if ($request_uri ~* "^/(?:ee_wizard|admin\.php|member|register|login|checkout)") {
set $secure_area "1";
}
# referer same domain, needs-to-be-secure area and via https [configure]
# NOTE: the base domain needs to be hard-coded, as it is in a regex (no variable expansion support)
set $secure_referer "0";
if ($http_referer ~* "https://.*domain.com.*/(?:ee_wizard|admin\.php|member|register|login|checkout).*") {
set $secure_referer "1";
}
# referer is needs-to-be-secure login or member area [configure]
# NOTE: the base domain needs to be hard-coded, as it is in a regex (no variable expansion support)
set $secure_member_referer "0";
if ($http_referer ~* "https://.*domain.com.*/(?:member|register|login).*") {
set $secure_member_referer "1";
}
## concatenated tests and redirects ##
# decide whether you want redirects to be 'permanent' (HTTP Status 301)
# or 'last' (temp, HTTP Status 302) [configure below]
# redirect old index.php requests (like from externally cached URLs, *before* removing index.php)
# (this will not cause an infinite loop when rewriting index.php-less /segment/segment to /index.php/segment/segment
# because server block 'ifs' are read before any location block, and the 'last' rewrite for prepending index.php
# to /segment/segment URLs skips this server block 'if' when internally redirecting, i.e. goes directly to
# 'location ~* \.php')
set $req "${get_req}${indexphp_req}";
if ($req = "11") {
rewrite ^/index\.php/*(.*) $scheme://$host/$1? permanent;
}
# redirect needs-to-be-secure sections or pages to https
set $req "${secure_req}${secure_area}";
if ($req = "01") {
rewrite ^ https://$host$request_uri? permanent;
}
# allow actions (e.g. /?css=global_embeds/site_css.v.1305076786) if referenced from https page
set $req "${secure_req}${query_req}${secure_area}${secure_referer}";
if ($req = "0101") {
rewrite ^ https://$host$request_uri? permanent;
}
# skip '/' matches where posted data is sent to hidden 'index.php'
# (required for login/register notices)
set $req "${secure_req}${root_req_only}${post_req}${secure_member_referer}";
if ($req = "1111") {
break;
}
# redirect all needs-not-be-secure back to http
# NOTE: careful that https resources/actions don't revert back to http
# (will stay https if linked-to resource has query)
set $req "${secure_req}${query_req}${secure_area}${static_resource}";
if ($req = "1000") {
rewrite ^ http://$host$request_uri? permanent;
}
## Block access to specific system and template areas ##
# make admin tools accessible via specific IPs only [configure]
location ~* "^/(?:ee_wizard|admin\.php)" {
# uncomment 'allow' and 'deny' and replace 'allow' with your IP address(es)
# allow 127.0.0.1;
# deny all;
# see also: 'location ~* \.php' below
location ~* \.php {
# NOTE: this redundant php config is necessary for access blocking, or
# php files are served as-is, i.e. not interpreted by php process
# include ee_fastcgi_params BEFORE fastcgi_pass
include incl.d/ee_fastcgi_params;
# un/comment per connection method to standalone php process (only use one)
# fastcgi_pass 127.0.0.1:9000;
fastcgi_pass unix:/etc/php5/fpm/sockets/my-domain-com.sock;
}
}
# following match similar to .htaccess files with 'deny from all'
# block direct access to system folder (if in www space) [configure]
# NOTE: rewrite my-system-dir-name to your custom name
location ~* "^/(?:my-system-dir-name|system)" {
deny all;
}
# block direct access to specific site 'internal include-only' theme resources [configure]
# NOTE: this is setup for the way the demo ee site is config'd... yours may vary
location ~* "^/themes/site_themes/.*/(?:.+\.group|snippets|global_variables)/" {
deny all;
}
## Pass regular /index.php/segment/... and other php scripts to php ##
# requires 'try_files $uri $uri/ @eefrontend;' in location block
# (usually 'location /') in main server config
location @eefrontend {
# internal rewrite /segment/segment/... to /index.php/segment/...
rewrite ^ /index.php$request_uri? last;
}
# see also: 'location ~* \.php' above
location ~* \.php {
# NOTE: regex is not \.php$, this allows for ee_wizard to work
# and other segmented URLs like: /segment/file.php/segment/segment
# secure uploads dir, see: http://wiki.nginx.org/PHPFcgiExample,
# uploads (or its parent) directory name [configure]
# NOTE: this does not need to be in config for nested 'location ~* \.php'
# in specific-IPs access to admin tools above
if ($uri ~* "^/images/") {
return 403;
}
# include ee_fastcgi_params BEFORE fastcgi_pass
include incl.d/ee_fastcgi_params;
# un/comment per connection method to standalone php process (only use one)
# fastcgi_pass 127.0.0.1:9000;
fastcgi_pass unix:/etc/php5/fpm/sockets/my-domain-com.sock;
}
This post is a report on my recent experiment getting EE to run well on a Nginx/PHP-FPM/PHP5.3.3+, no-Apache2 setup. I'm new to EE, but not server administration, with plenty of experience using Apache2.
However, after seeing the excessive RAM usage by Apache2 on my Ubuntu 10.04 LTS virtual server, (ve) @ MediaTemple.net , I wanted to look into deploying my first EE site on a more lightweight, but still robust setup, capable of scaling well. After working with EE for a bit, and finding out just how cool it is, I thought I'd share my results of trying this with Nginx 1.x.
The point of this post is to garner input from other EE server admins to refine these Nginx configs, with the hopeful outcome of generating, as much as possible, a 'config-and-drop-in' Nginx setup. While setting up current versions of Nginx and PHP-FPM are not within the scope of this posting...
[b]On Ubuntu 10.0.4[/b], you may want to check out:
https://launchpad.net/~nginx
https://launchpad.net/~brianmercer/+archive/php5
(PHP-FPM is now part of PHP5.3.3+, but only PHP5.3.2 is supported for Ubuntu 10.04, unless you install Brian Mercer's version via his PPA. More recent versions of Ubuntu Desktop and Server have full PHP5.3.3+ and PHP-FPM support.)
[b]On a Mac[/b], you can install everything via MacPorts, but make sure to launch Nginx under different ports, due to Web Sharing running under the default 80 and 443. Or, turn off Web Sharing.
http://www.macports.org/ports.php?by=name&substr=nginx
http://www.macports.org/ports.php?by=name&substr=php5
These configs do not require PHP-FPM, and should work with any PHP fastcgi upstream server that Nginx supports.
[size=3][b]Currently, my first successful Nginx configs support the following on a fresh EE 2.4 out-of-the-box 'Agile Records' demo install:[/b][/size]
[quote]
* 100% ee_wizard success - http://expressionengine.com/overview/requirements
* Support for index.php-less segmented, no-query, SEO URLS
* Support for AUTO and PATH_INFO segment recognition
* Auto server-based http->https and https->http for custom segments, EE login actions, admin control panel, including complete https resources (i.e. no partial https pages)
* Methodology to recreate similar syntax of many Apache rewrite configs
* Request IP restriction to admin.php and custom segments
* Support for several security considerations: block access to system directory, protect against attempted user-uploaded php, .htaccess-like blocking for site's internal template resources
* Requires only a two-line edit of /system/expressionengine/config/config.php (no other EE code adjustment or add-on needed)[/quote]
[quote]The Nginx configs are available as a Gist repository:
https://gist.github.com/2009943
Read the README.txt for initial EE and server setup before attempting to use the configs.[/quote]
[b]PLEASE DO NOT USE THESE ON A PRODUCTION SERVER[/b] unless you're the carefree sort of admin that doesn't mind breaking things. To use these configs, you should have a decent understanding of regex, Nginx configuration, and fastcgi proxying. Again, the point of this post is to work towards a [b]stable[/b] set of Nginx configs for most use cases, not esoteric ones.
In subsequent posts I would like to discuss aspects of using these configs, though they are pretty well documented in their comments already. The focus will be to try to NOT edit EE, but to have the server configs handle the support. So, obviously this will be somewhat impossible in a shared hosting setup. Access to configuring Nginx and the PHP fastcgi process is required.
[b]Caveat #1[/b]: Since I'm new to EE, I have not built an extensive site, yet. I hope other developers/admins might test copies of more complicated existing sites with this setup, so that the configs can be adapted to 'real world' cases, beyond the demo install.
[b]Caveat #2[/b]: The configs do NOT currently support: multi-site setups or running EE from a sub-folder. These are features that I believe can be incorporated (or may even work), but I do not have those setups to test.
[b]Credits[/b]: These configs are based, in part, upon the existing work of others...
http://feistygoat.com/blog/2010/10/force-ssl-in-expressionengine/
http://pixelandtonic.com/blog/making-pt-structure
http://kbeezie.com/view/php-self-path-nginx/
http://kbeezie.com/view/apache-to-nginx/
http://us.php.net/manual/en/install.fpm.php
http://wiki.nginx.org/PHPFcgiExample
http://wiki.nginx.org/Pitfalls
http://forum.nginx.org/read.php?2,88845,page=3
[i]Do you think I'd be responsible for anything you did with these configs? I didn't think so, 'cause you're the type of person who accepts, or dare I say, enjoys, such risks.[/i]
Regards, and have fun,
Larry S
# on Ubuntu 10.04 LTS server with backported, but latest, PHP 5.3.x
# Nginx available here: https://launchpad.net/~nginx
# PHP5.3.x (latest with native php-fpm): https://launchpad.net/~brianmercer/+archive/php5
# NOTE: I created and use an /etc/nginx/incl.d directory to store my included configs
# *THIS IS NOT A COMPLETE CONFIGURATION* (other configurations removed for clarity)
server {
# replace with your IP address(es)
listen 127.0.0.1;
listen 127.0.0.1:443 ssl;
server_name my.domain.com;
include incl.d/my-domain-com_cert;
access_log /var/log/nginx/my-domain-com-access.log;
error_log /var/log/nginx/my-domain-com-error.log error;
root /www/sites/my-domain-com;
# set indexes at server level
index index.html index.php;
# ee support (blocking, https, etc.)
# include before any location blocks
# do NOT include inside a location block
include incl.d/ee_support_my-domain-com;
location / {
# serve regular files, without passing to php
try_files $uri $uri/ @eefrontend;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment