Of course I have a backup!

Random blobs of wisdom about software development

Nginx configuration for Symfony2

Saturday, November 17, 2012

Every time I have to set up Symfony2 on a new machine, I get a bit lost on the rewrite configuration. Rewriting in apache is easy, since the net is already full of examples on how to do it, but there is nearly not as much info for nginx (yet).

First of all, you can find several guides that are wrong, unsecure, or include unnecessary directives, on the first page, when googling for "symfony2 on nginx". The mistakes these guides are making, are all explained in the pitfalls section of the nginx documentation, namely:

  • Unnecessary "location /" directives
  • Unnecessary if statements
  • Unnecessary rewrites
  • Forwarding every .php file to PHP

The first 3 are simply a performance issue, on a development machine you might not really care about it. The 4th is major though, since it's a security issue, that comes from a line like this:

location ~ .php$ {

If visitors can upload files through your site, and you place those under the document root, and a .php file gets through your filtering, it will allow people to run arbitrary code on your webserver. Even if you don't allow, or secure your uploads, there is still a vulnerability around PHP's ini setting cgi.fix_pathinfo (which is turned on by default), that can bite you. That's a lot of ifs, and whens, but still, why not attack the problem at the root. Also, it might be a good idea to read the if is evil section of the docs.

So refining a bit on the example configuration on the nginx page, and keeping the pitfalls in mind, this is probably the most compact, and secure Symfony2 config for nginx:

server {
  server_name symfony2.local;
  root /srv/http/symfony2/web;
  index app.php;
  try_files $uri $uri/ /app.php?$query_string;
  location ~ ^/(app_dev|app_test.php|app)\.php(/|$) {
    include fastcgi_params;
    fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param HTTPS off;

If you are using this for development, replace the app.php in the index , and try_files directives with app_dev.php. On production, you can remove the app_dev.php from the location directive. It might also be a good idea to throw in an error_log in the server block. Keep in mind that this only allows the app.php, and app_dev.php files to run, every other PHP file will get sent to the client as plain text, but you shouldn't be placing other PHP files there anyway, since you are using a front controller. It will be a problem if you use the the web based configurator (config.php) script though.

The differences between this and the config on the nginx wiki, is that the wiki's example includes two location directives, and some rewrite magic, that could be left out, and there is a fastcgi_split_path_info line, that is probably supposed to work around the vulnerability that I mentioned above, but since we are only allowing the app_dev.php, and app.php files in the document root to be executed, it is also unnecessary. There is also a line that deals with stripping the app.php from the URL if it is present, but it didn't seem to work for me (infinite redirect).

Upstream sent too big header while reading response header from upstream

If you get internal server errors for some pages, and see the above line in your logs, it means that the application is sending too many and/or too long headers. In Symfony, this is 99% of the time caused by FirePHP, since it sends everything in the headers. This is usually not a problem, since by default there are just a few lines of logs, but after you start writing loggers for your own logic, they might get fat, and nginx will choke on it. The solution is to include the two lines in your location block (adjust the numbers until it works):

fastcgi_buffer_size 16k;
fastcgi_buffers 4 16k;

This was written by Norbert Kéri, posted on Saturday, November 17, 2012, at 17:37

Tagged as:
hacfi wrote
Thanks for sharing this! I just switched from Apache to nginx for my development environment (have been using nginx in production for a while) and this is the easiest solution around!

2012-11-20 01:50:34

eBuildy wrote
Nice article, good for à basic web site but for instance, 90% of réal web site serves static (js, css, image..) from à différent server, so first of all you must havé 2 différent config, one only for Php and thé other for static.

2012-11-21 19:42:11

Bill Surgenor wrote
The try_files directive will serve any static files quite nicely

2012-11-24 02:49:34

Kate wrote
Thanks for sharing! This info was included into a digest of the best PHP news and releases: http://www.zfort.com/blog/php-digest-december-4-2012/ Check it out, I bet this digest is going to be interesting for you!

2012-12-07 13:03:58

Paul Mansell wrote
This setup works brilliantly - im using it in production .. now i have to add a second Symfony2 project running on the same host - its going to be accessed using an alias ... so http://localhost/ goes to the first site .. and http://localhost/another should go to the second - what config can i use to do that ?

2013-02-14 16:42:05

Norbert Kéri wrote
@Paul Mansell:
I'm just guessing here, but it should be possible doing rewriting based on the first part of the uri (the word after the first slash in the url). Create a second virtualhost, and rewrite requests to that.

2013-02-15 11:47:29

Joe wrote
You could try integrating the rewrite rule from the wiki modified as follows:
rewrite ^/app\.php/?(.+)$ /$1 permanent;

This works fine for me.

2013-05-02 22:34:54

Johnny Robeson wrote
consider changing

try_files $uri $uri/ /app.php?$query_string;

try_files $uri $uri/ /app.php$is_args$args;

$is_args will use ? when required and $query_string is the same thing as $args

2014-08-31 09:17:31

Norbert Kéri wrote
Nice, didn't know about $is_args.

2014-09-10 02:12:04

Robert wrote
Thanks, this was the only one I found that actually worked. :-)

2015-11-09 16:37:11

Post a comment

Providing your email is optional, it is never published or shared, it is only used for auto approval purposes. If you already have at least 1 approved comment(s) tied to your email, you don't have to wait for moderation, otherwise the author must approve your comment.

Please solve this totally random captcha