<?xml version="1.0" encoding="utf-8" ?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Fun with ones and zeros - scgi</title>
<subtitle>Barry&#039;s notes on computer software and hardware</subtitle>
<link href="/blog/tags/scgi"></link>
<updated>2026-06-13T04:24:49-07:00</updated>
<id>urn:uuid:70af3f26-3b4f-288d-2871-c093ebffd639</id>
<entry>
<title>Running Python WSGI apps with SCGI and inetd</title>
<link href="/blog/entries/running-python-wsgi-apps-scgi-and-inetd"></link>
<id>urn:uuid:17ba402d-28d0-1844-298e-26ae6796046b</id>
<updated>2011-09-18T06:00:00-07:00</updated>
<author><name>Barry Pederson</name>
<email>bp@barryp.org</email>
</author>
<content type="html">&lt;body&gt;&lt;p&gt;&lt;em&gt;Using &lt;a href=&quot;https://github.com/barryp/scgi-inetd-wsgi&quot;&gt;scgi-inetd-wsgi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Previously, I wrote about running &lt;a href=&quot;/blog/entries/cgi-scripts-nginx-using-scgi/&quot;&gt;CGI Scripts with Nginx using SCGI&lt;/a&gt;
with the help of a super-server such as &lt;code&gt;inetd&lt;/code&gt; and a small C shim that
takes a SCGI request from stdin and sets up a CGI enviroment.&lt;/p&gt;
&lt;p&gt;There&#039;s also a companion project &lt;a href=&quot;https://github.com/barryp/scgi-inetd-wsgi&quot;&gt;on GitHub&lt;/a&gt; for doing something
similar with Python WSGI apps.  The code works on Python 2.6 or higher
(including Python 3.x).  &lt;em&gt;It can easily be patched for Python 2.5 or lower
by with a simple string substitition mentioned in the source file&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It&#039;s not something you&#039;d want to run a frequently-accessed app with,
because there&#039;d be quite a bit of overhead launching a Python process to
handle each request.  It may be useful however for infrequently used
apps where you don&#039;t want to have to keep and monitor a long-running
process, or for development of a WSGI app where you don&#039;t want to have
to stop/start a process everytime you make a change.&lt;/p&gt;
&lt;p&gt;Let&#039;s take a look at a diagram to see what the flow will be:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/10/23/scgi_wsgi.png&quot; alt=&quot;SCGI&amp;lt;-&amp;gt;WSGI&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Nginx opens a socket listened to by inetd&lt;/li&gt;
&lt;li&gt;inetd spawns a Python script with stdin and stdout connected to the
accepted connection&lt;/li&gt;
&lt;li&gt;The Python script would import &lt;code&gt;inetd_scgi&lt;/code&gt; and call its &lt;code&gt;run_app&lt;/code&gt; function
passing a WSGI app to actually handle the request.  &lt;code&gt;run_app&lt;/code&gt; will read
the SCGI request from stdin, setup a WSGI enviroment, call the handler, and
send the handler&#039;s response back to Nginx via stdout.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&#039;s how you&#039;d wire up the &lt;code&gt;Hello World&lt;/code&gt; app from &lt;a href=&quot;http://www.python.org/dev/peps/pep-3333/&quot;&gt;PEP 3333&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;source&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;HELLO_WORLD&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Hello world!&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;simple_app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&quot;&quot;&quot;Simplest possible application object&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#039;200 OK&#039;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response_headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#039;Content-type&#039;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#039;text/plain&#039;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response_headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HELLO_WORLD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#039;__main__&#039;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;inetd_scgi&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;inetd_scgi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run_app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;simple_app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you had saved that script as say &lt;code&gt;/local/test.py&lt;/code&gt;, you might add this to
&lt;code&gt;/etc/inetd.conf&lt;/code&gt; to serve it up:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:www:www:200:/var/run/test.sock  stream   unix   nowait/4  www /local/test.py /local/test.py&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and in Nginx with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;location /test {
    scgi_pass unix:/var/run/test.sock;
    include /usr/local/etc/nginx/scgi_params;
    fastcgi_split_path_info ^(/test)(.*);
    scgi_param  SCRIPT_NAME $fastcgi_script_name;
    scgi_param  PATH_INFO $fastcgi_path_info;
}    &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, accessing &lt;a href=&quot;http://localhost/test&quot;&gt;http://localhost/test&lt;/a&gt; should show &#039;Hello world!&#039;&lt;/p&gt;&lt;/body&gt;</content>
</entry>
<entry>
<title>AWStats under Nginx and SCGI</title>
<link href="/blog/entries/awstats-under-nginx-and-scgi"></link>
<id>urn:uuid:17c6cd77-c49f-74d7-5b49-94439fda5615</id>
<updated>2011-09-14T06:00:00-07:00</updated>
<author><name>Barry Pederson</name>
<email>bp@barryp.org</email>
</author>
<content type="html">&lt;body&gt;&lt;p&gt;Earlier, I wrote about running &lt;a href=&quot;/blog/entries/cgi-scripts-nginx-using-scgi&quot;&gt;CGI Scripts with Nginx using SCGI&lt;/a&gt;
with the help of a small C shim.  One particular CGI app I&#039;ve had to
alter slightly to work under this setup is &lt;a href=&quot;https://awstats.sourceforge.io/&quot;&gt;AWStats&lt;/a&gt;, which
is a decent-sized Perl app, but only requires one line added to satisfy
SCGI&#039;s requirement of a &lt;code&gt;Status&lt;/code&gt; line at the beginning of a response.&lt;/p&gt;
&lt;p&gt;Here&#039;s a patch to AWStats 7.0&lt;/p&gt;
&lt;div class=&quot;source&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--- awstats.pl.original 2011-09-11 21:20:40.954555528 -0500&lt;/span&gt;
&lt;span class=&quot;gi&quot;&gt;+++ awstats.pl  2011-03-31 00:19:35.867343845 -0500&lt;/span&gt;
&lt;span class=&quot;gu&quot;&gt;@@ -750,6 +750,7 @@&lt;/span&gt;
 #------------------------------------------------------------------------------
 sub http_head {
        if ( !$HeaderHTTPSent ) {
&lt;span class=&quot;gi&quot;&gt;+                print &quot;Status: 200 OK\n&quot;;&lt;/span&gt;
                my $newpagecode = $PageCode ? $PageCode : &quot;utf-8&quot;;
                if ( $BuildReportFormat eq &#039;xhtml&#039; || $BuildReportFormat eq &#039;xml&#039; ) {
                        print( $ENV{&#039;HTTP_USER_AGENT&#039;} =~ /MSIE|Googlebot/i
&lt;/pre&gt;&lt;/div&gt;&lt;/body&gt;</content>
</entry>
<entry>
<title>CGI Scripts with Nginx using SCGI</title>
<link href="/blog/entries/cgi-scripts-nginx-using-scgi"></link>
<id>urn:uuid:8ea0980f-ef5c-41bb-a921-9f5db644ede3</id>
<updated>2011-09-11T15:37:00-07:00</updated>
<author><name>Barry Pederson</name>
<email>bp@barryp.org</email>
</author>
<content type="html">&lt;body&gt;&lt;p&gt;&lt;em&gt;Using &lt;a href=&quot;https://github.com/barryp/scgi-inetd-cgi&quot;&gt;scgi_run&lt;/a&gt; with Nginx&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.nginx.com/resources/wiki/&quot;&gt;Nginx&lt;/a&gt; is a great web server, but one thing it doesn&#039;t support
is &lt;a href=&quot;https://en.wikipedia.org/wiki/Common_Gateway_Interface&quot;&gt;CGI scripts&lt;/a&gt;.  Not all webapps need to be high-performance
setups capable of hundreds or thousands of requests per second.  Sometimes
you just want something capable of handling a few requests now and then,
and don&#039;t want to keep a long-running process going all the time just
for that one webapp.  How do you handle something like that under Nginx?&lt;/p&gt;
&lt;p&gt;Well, it turns out you&#039;re going to have to have &lt;em&gt;something&lt;/em&gt; running as a
long-running external process to help Nginx out (because Nginx can&#039;t
spawn processes itself).  It just doesn&#039;t have to be dedicated to any
one particular webapp.  One way to go would be to setup &lt;em&gt;another&lt;/em&gt;
webserver that &lt;em&gt;can&lt;/em&gt; do CGI scripts, and have Nginx proxy to that when
need be.  &lt;/p&gt;
&lt;p&gt;Apache is one possibility, something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/9/21/nginx_apache.png&quot; alt=&quot;Nginx &amp;lt;-&amp;gt; Apache&quot;&gt;&lt;/p&gt;
&lt;p&gt;But Apache&#039;s a fairly big program, has lots of features, a potentially
complicated configuration.  Kind of defeats the purpose of going to a
lighter-weight program like Nginx.  What else can we do?&lt;/p&gt;
&lt;h2&gt;Super-servers&lt;/h2&gt;
&lt;p&gt;Many Unix-type systems will have a &lt;em&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Super-server&quot;&gt;super-server&lt;/a&gt;&lt;/em&gt;
available to launch daemons as need be when some network connection is
made.  On BSD boxes it&#039;s typically &lt;code&gt;inetd&lt;/code&gt;, MacOSX has &lt;code&gt;launchd&lt;/code&gt;, Linux
distros often have &lt;code&gt;xinetd&lt;/code&gt; or other choices available.  &lt;/p&gt;
&lt;p&gt;If we already have a super-server running on our box, why not setup
Nginx to connect to that, and let the super-server take care of launching
our CGI script?  We just need one extra piece of the puzzle, something to
read a web request over the socket Nginx opened up, setup the CGI
environment, and execute the script.  &lt;/p&gt;
&lt;p&gt;Wait, that sounds like a web server - aren&#039;t we back to something like
Apache again?  No, it doesn&#039;t have to be anything nearly that
complicated if we were to use the &lt;a href=&quot;https://en.wikipedia.org/wiki/Simple_Common_Gateway_Interface&quot;&gt;SCGI&lt;/a&gt; protocol, instead of HTTP.&lt;/p&gt;
&lt;h2&gt;SCGI&lt;/h2&gt;
&lt;p&gt;SCGI is a very simple protocol that&#039;s &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_scgi_module.html&quot;&gt;supported by Nginx&lt;/a&gt;
and many other webservers.  It&#039;s much much simpler than FastCGI, and
maps pretty closely to the CGI specfication, with one minor difference
to note...&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://www.ietf.org/rfc/rfc3875.txt&quot;&gt;CGI RFC&lt;/a&gt;, the response may contain an optional Status line,
as in:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Status: 200 OK&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the SCGI protocol, the &lt;code&gt;Status&lt;/code&gt; line is required, not optional.  &lt;/p&gt;
&lt;p&gt;&lt;em&gt;Nginx will function with the &lt;code&gt;Status&lt;/code&gt; line missing, but there&#039;ll be
warnings in your error log.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you can alter your CGI scripts to include a &lt;code&gt;Status&lt;/code&gt; line, or live with
warnings in logs, we have a way forward now.&lt;/p&gt;
&lt;h2&gt;scgi_run&lt;/h2&gt;
&lt;p&gt;I&#039;ve got a C &lt;a href=&quot;https://github.com/barryp/scgi-inetd-cgi&quot;&gt;project on GitHub&lt;/a&gt; that implements this small piece of
glue to turn a SCGI request into a CGI enviroment.  The binary weighs in at
around 8 to 12 Kilobytes after being stripped.&lt;/p&gt;
&lt;p&gt;Basically, we&#039;re looking at a flow like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/9/22/nginx_scgi.png&quot; alt=&quot;Nginx &amp;lt;-&amp;gt; SCGI&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Nginx connects to a socket listened to by inetd&lt;/li&gt;
&lt;li&gt;inetd spawns &lt;code&gt;scgi_run&lt;/code&gt;, with stdin and stdout wired to the accepted
connection&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scgi_run&lt;/code&gt; reads SCGI request headers from stdin and sets up a CGI environment&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scgi_run&lt;/code&gt; execs CGI script (stdin and stdout are still connected to the
socket to Nginx)&lt;/li&gt;
&lt;li&gt;CGI script reads request body if necessary from stdin and writes
response out through stdout.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A couple things to note here &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;when we get to the final step, the CGI script is talking directly to
Nginx - there&#039;s no buffering by any other applications like there would be
in an Apache setup.  &lt;/li&gt;
&lt;li&gt;scgi_run is no longer executing, it execed the CGI script so there&#039;s
not another process hanging around waiting on anything.&lt;/li&gt;
&lt;li&gt;A super-server like inetd can typically be configured to run the handler
under any userid you want, so you basically get SUEXEC-type functionality for
free here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;scgi_run&lt;/code&gt; code on GitHub operates in two modes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If argv[1] ends with a slash &lt;code&gt;/&lt;/code&gt;, then argv[1] is taken to be a directory
name, and the program will look for the &lt;code&gt;SCRIPT_FILENAME&lt;/code&gt; passed by Nginx
in that directory.&lt;/li&gt;
&lt;li&gt;Otherwise, argv[1] is taken as the path to a specific CGI script
(so &lt;code&gt;SCRIPT_FILENAME&lt;/code&gt; is ignored), and any additional arguments are passed
on to the CGI script.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Configuration&lt;/h2&gt;
&lt;p&gt;A simple setup looks something like this, assuming you&#039;ve compiled &lt;code&gt;scgi_run&lt;/code&gt; and have
the binary stored as &lt;code&gt;/local/scgi_run&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;For FreeBSD &lt;code&gt;inetd&lt;/code&gt; for example, you might add a line to &lt;code&gt;/etc/inetd.conf&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:www:www:600:/var/run/scgi_localcgi.sock stream  unix    nowait/16   www /local/scgi_run /local/scgi_run /local/cgi-bin/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which causes &lt;code&gt;inetd&lt;/code&gt; to listen to a Unix socket named
&lt;code&gt;/var/run/scgi_localcgi.sock&lt;/code&gt;, and when a connection is made, it spawns
&lt;code&gt;/local/scgi_run&lt;/code&gt; with argv[0] set to &lt;code&gt;/local/scgi_run&lt;/code&gt; and argv[1] set
to &lt;code&gt;/local/cgi-bin/&lt;/code&gt;.  As a bonus, the socket ownership is set to www:www
and chmoded to 0600, which limits who can connect to it.&lt;/p&gt;
&lt;p&gt;In Nginx, you might have something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;location /local-cgi/ {
    alias /local/cgi-bin/;

    scgi_pass unix:/var/run/scgi_localcgi.sock;
    include /usr/local/etc/nginx/scgi_params;
    scgi_param  SCRIPT_NAME $fastcgi_script_name;
    scgi_param  PATH_INFO $fastcgi_path_info;
    scgi_param  SCRIPT_FILENAME $request_filename;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then for a simple script, you might have &lt;code&gt;/local/cgi-bin/hello.sh&lt;/code&gt; as&lt;/p&gt;
&lt;div class=&quot;source&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;ch&quot;&gt;#!/bin/sh&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Status: 200 OK&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: text/plain&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That you would run by hitting &lt;code&gt;http://localhost/local-cgi/hello.sh&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So, with the help of a tiny 8KB binary, Nginx (or any other SCGI client)
with the help of a super-server like &lt;code&gt;inetd&lt;/code&gt; can execute CGI scripts
(keeping in mind though the requirement for the &lt;code&gt;Status&lt;/code&gt; line).  It&#039;s a
fairly lightweight solution that may also be useful in embedded situations.&lt;/p&gt;
&lt;p&gt;Enjoy, and &lt;a href=&quot;/&quot;&gt;go buy some harddrives&lt;/a&gt; to store your CGI scripts on, I hear
SSDs are very nice. :)&lt;/p&gt;&lt;/body&gt;</content>
</entry>
<entry>
<title>mod_scgi redirection</title>
<link href="/blog/entries/modscgi-redirection"></link>
<id>urn:uuid:0b5e2ff0-4da0-dc79-1223-ac5eb77df393</id>
<updated>2007-01-11T10:39:38-08:00</updated>
<author><name>Barry Pederson</name>
<email>bp@barryp.org</email>
</author>
<content type="html">
&lt;p&gt;While working on a new Django project, I noticed something odd about running it under mod_scgi: if you were POSTing to a URL, &lt;code&gt;/foo&lt;/code&gt; for example, and the view for that URL did a relative redirect, as in &lt;code&gt;django.http.HttpResponseRedirect(&#039;/bar&#039;)&lt;/code&gt;, the 302 redirect wasn&#039;t making it back to the browser.  Instead, the browser was acting like the result of &lt;code&gt;POST /foo&lt;/code&gt; was a &lt;code&gt;200 OK&lt;/code&gt; followed by the data you&#039;d receive from  &lt;code&gt;GET /bar&lt;/code&gt;, without the browser knowing that it coming from a new location.  The big drawback to this is that if you do a reload, the browser tries to POST to &lt;code&gt;/foo&lt;/code&gt; again, instead of just &lt;code&gt;GET /bar&lt;/code&gt;.  The Django docs recommend always responding to POSTs with redirects, just for this reason.
&lt;/p&gt;
&lt;p&gt;Strictly speaking, redirects &lt;em&gt;should&lt;/em&gt; be absolute URLs  (see section &lt;a href=&quot;http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html&quot;&gt;14.30&lt;/a&gt; in the HTTP specs), and if you use one of those, it acts as expected.   Django is full of relative redirects, but the framework at this time doesn&#039;t seem to try and convert them to absolute.  There is ticket &lt;a href=&quot;http://code.djangoproject.com/ticket/987&quot;&gt;#987&lt;/a&gt; in the Django Trac that talks about this a bit.  &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Browsers seem to handle relative redirects OK through, and that behavior doesn&#039;t occur with the Django test http server.  Having mod_scgi conceal what Django is doing is not so good.
&lt;/p&gt;
&lt;p&gt;Digging into the mod_scgi sourcecode &lt;code&gt;apache2/mod_scgi.c&lt;/code&gt; reveals a section of code that&#039;s causing this change:
&lt;/p&gt;
&lt;div class=&quot;source&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apr_table_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers_out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Location&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;&amp;#39;/&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HTTP_OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ap_is_HTTP_REDIRECT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;apr_brigade_destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;/* Internal redirect -- fake-up a pseudo-request */&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HTTP_OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;/* This redirect needs to be a GET no matter what the original&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;    * method was.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;    */&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apr_pstrdup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;M_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;ap_internal_redirect_handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Tossing that section of code causes mod_scgi to leave the relative redirects alone.
&lt;/p&gt;
</content>
</entry>
<entry>
<title>Django, SCGI, and AJP</title>
<link href="/blog/entries/django-scgi-and-ajp"></link>
<id>urn:uuid:e96047c1-a7d9-4f2a-c585-2270fe3d378d</id>
<updated>2006-11-20T18:33:44-08:00</updated>
<author><name>Barry Pederson</name>
<email>bp@barryp.org</email>
</author>
<content type="html">
&lt;p&gt;I&#039;ve been doing a lot with Django lately, and initially set it up using mod_python as the Django docs recommend, but still have some &lt;a href=&quot;/blog/entries/backend_protocols/&quot;&gt;reservations&lt;/a&gt; about that kind of arrangement.  I&#039;d like to go back to running it under SCGI or something similar.   &lt;br /&gt;
&lt;/p&gt;
&lt;p&gt;Django has support builtin for FastCGI, but after trying to install mod_fastcgi in my Apache 2.0.x setup, decided it was a PITA.  mod_scgi is quite easy to setup  in Apache (even though the documentation is mostly nonexistent).  After finding where Django implements its FastCGI support using the &lt;a href=&quot;http://www.saddi.com/software/flup/&quot;&gt;flup&lt;/a&gt; module, I saw that with just a few minor tweaks Django could be made to support all of flup&#039;s protocols, including SCGI and AJP (Apache Jserv Protocol).
&lt;/p&gt;
&lt;p&gt;AJP turns out to be very interesting because it&#039;s included standard with Apache 2.2 as &lt;a href=&quot;http://httpd.apache.org/docs/2.2/mod/mod_proxy_ajp.html&quot;&gt;mod_proxy_ajp&lt;/a&gt;, and can work with &lt;a href=&quot;http://httpd.apache.org/docs/2.2/mod/mod_proxy_balancer.html&quot;&gt;mod_proxy_balancer&lt;/a&gt; - meaning you could setup multiple Django instances and have Apache share the load between them.
&lt;/p&gt;
&lt;p&gt;After testing a bit, I &lt;a href=&quot;http://code.djangoproject.com/ticket/3047&quot;&gt;submitted a patch&lt;/a&gt;, and will probably switch to running my Django sites as AJP servers managed by daemontools, and frontended by Apache 2.2
&lt;/p&gt;


</content>
</entry>
</feed>