Project

General

Profile

Fastcgi on nginx » History » Revision 18

Revision 17 (Laurence Withers, 01/03/2015 04:41 PM) → Revision 18/31 (Laurence Withers, 01/03/2015 04:42 PM)

h1. FastCGI on nginx 

 {{toc}} 


 h2. About nginx  

 On paper, nginx is an excellent choice for use as a reverse proxy (together with wthttpd), or for deploying Wt applications using FastCGI, especially if you are using server-initiated updates (server push). 

 The reason is that nginx, like wthttpd, is completely based on asynchronous I/O, and therefore scales easily to many open connections (unlike other web servers such as Apache, lighttpd, etc.). Server-initiated updates use an open connection for every web client. 

 We have tested nginx succesfully for both its use as a reverse proxy or for FastCGI deployment of Wt applications. Its FastCGI implementation is a bit clumsy, but newer versions of nginx (>= 0.7.31) than the version currently available in ubuntu should improve on that. 


 h2. Ubuntu configuration 

 * Installing nginx and spawn-fcgi (which comes with lighttpd): 

 <pre> 
  $ sudo apt-get install nginx lighttpd 
 </pre> 

 * Start your application (linked against wtfcgi) using spawn-fcgi. Spawn-fcgi wraps the FastCGI application to listen to a socket or network connection. 

 <pre> 
  $ spawn-fcgi -n -f ../../build/examples/hello/hello.wt -a 0.0.0.0 -p 9091 
 </pre> 

 * Edit your nginx configuration to redirect a particular URL to your application (replace all occurences of /hello.wt with the path for your application!): 

 <pre> 
         location /hello.wt { 
                 fastcgi_pass     127.0.0.1:9091; 
 
                 fastcgi_param    QUERY_STRING         $query_string; 
                 fastcgi_param    REQUEST_METHOD       $request_method; 
                 fastcgi_param    CONTENT_TYPE         $content_type; 
                 fastcgi_param    CONTENT_LENGTH       $content_length; 
 
                 if ($document_uri ~ "^/hello.wt/(.*)") { 
                         set $apache_path_info /$1; 
                 } 
 
                 fastcgi_param    SCRIPT_NAME          /hello.wt; 
                 fastcgi_param    PATH_INFO            $apache_path_info; 
                 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/$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; 
         } 
 </pre> 

 * Alternatively to avoid 'if' in declaration just indicate the root of your application: 
 <pre> 
         location ~ ^/hello.wt(?<path_info>/.*|$) { 
                 fastcgi_pass     127.0.0.1:9000; 
                 fastcgi_param    SCRIPT_NAME /hello.wt; 
                 fastcgi_param    PATH_INFO $path_info; 
                 include          fastcgi_params.conf; 
         } 
 </pre> 

 * Reload (or restart) your nginx server and you are ready to go: 

  $ sudo /etc/init.d/nginx restart 



 h2. Debian (Wheezy) configuration 

 This configuration uses the FastCGI support of nginx, which is enabled in both of the available Debian packages nginx-light and nginx-full. Unlike apache/lighttpd, nginx will not directly spawn the FastCGI application, and so we need an intermediate package to take care of this for us. Fortunately, the end result is very neat, and allows the fastcgi application to be restarted independently of the web server (as well as to easily enable it to run under a different uid). 

 h3. spawn-fcgi 

 First, install the spawn-fcgi package: 

 <pre> 
  $ sudo apt-get install spawn-fcgi 
 </pre> 

 This package is responsible for getting the Wt application (a fastcgi application) to listen on a unix socket, which nginx will be configured to talk to. (Note that this does not involve any sort of proxying/copying: spawn-fcgi only needs to set up sockets before executing our application directly). 

 The application may be started with a command such as: 

 <pre> 
  $ spawn-fcgi -s /var/run/fastcgi-<app>.sock -- /path/to/app.wt 
 </pre> 

 (obviously replacing the paths as appropriate). The environment variable @WT_APP_ROOT@ can, if desired, be set to point to a directory containing @wt_config.xml@. This is also exposed as Wt::WApplication::appRoot(). 

 h3. spawn-fcgi systemd unit files 

 If using systemd, you can trivially create a unit file under @/etc/systemd/system@, named @/etc/systemd/system/fastcgi-<app>.service@: 

 <pre> 
 [Unit] 
 Description=FastCGI spawner for amazing Wt web app 

 [Service] 
 ExecStart=/usr/bin/spawn-fcgi -s /var/run/fastcgi-<app>.sock \ 
         -P /var/run/fastcgi-<app>.pid \ 
         -u <username> -g <primary-group> -G www-data -M 0660 \ 
         -- /path/to/app.wt 
 Environment="WT_APP_ROOT=/path/to/app/root" 

 Type=forking 
 PIDFile=/var/run/fastcgi-<app>.pid 
 Restart=always 
 SyslogIdentifier=fastcgi-<app> 

 # Security-conscious users may wish to research: 
 #     PrivateTmp, PrivateDevices, ProtectSystem, ProtectHome 
 #     CapabilityBoundingSet= 
 #     NoNewPrivileges 

 [Install] 
 WantedBy=multi-user.target 
 </pre> 

 Again, replace the paths, usernames etc. as appropriate. In the @ExecStart@ line: 
 * @-s <socket>@ is the Unix socket over which nginx will contact your application 
 * @-P <path>@ is the PID file that will be monitored by systemd (so it must match the @PIDFile=@ line further below in the unit file) 
 * @-u <username>@ and @-g <primary-group>@ set the UID/GID to run the application as; this avoids needing to run your application as root at any point 
 * @-G www-data@ sets the group for the Unix socket; obviously it must allow read/write access for nginx (which by default uses the @www-data@ group). Since the socket does not have to have the same permissions as the application, this means that nginx can trivially run under a different uid/gid from your application. 
 * @-M 0660@ sets the mode on the Unix socket; here we set it to allow group read/write. 

 The @WT_APP_ROOT@ environment variable is set to allow an application-specific @wt_config.xml@ file to be placed in a specific directory, rather than relying on a system-wide @/etc/wt_config.xml@. The value of this variable is also exposed as Wt::WApplication::appRoot(). 

 Once set up, the service may be enabled at system startup with: 

 <pre> 
  $ sudo systemctl enable fastcgi-<app>.service 
 </pre> 

 It must be started manually for the first time (use @systemctl start@), and may at any time be started/stopped/restarted/disabled in the same manner. 

 

 h3. nginx configuration 

 Under the relevant @server@ section, simply add the following: 

 <pre> 
   location /my/app.wt { 
     fastcgi_pass unix:/var/run/fastcgi-<app>.sock; 
     include fastcgi_params; 
     fastcgi_split_path_info ^(/my/app.wt)(.*)$; 
     fastcgi_param PATH_INFO $fastcgi_path_info; 
   } 
 </pre> 

 The @fastcgi_pass@ line hands off the connection to our Wt application via the fastcgi protocol over a Unix socket. Obviously adjust the path as appropriate. 

 In order to get internal paths working properly under all situations (including when the application is installed at the server root), we need to set up a regular expression on the request URI with two captures. The first capture is used as the script name, and the second as the internal path. This is the purpose of the @fastcgi_split_path_info@ line and the @PATH_INFO@ parameter. The first capture is made available as @Wt::WEnvironment::deploymentPath()@ and the second as @Wt::WEnvironment::internalPath()@.