<?xml version="1.0" encoding="utf-8" ?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Fun with ones and zeros - nginx</title>
<subtitle>Barry&#039;s notes on computer software and hardware</subtitle>
<link href="/blog/tags/nginx"></link>
<updated>2026-04-29T03:13:38-07:00</updated>
<id>urn:uuid:50b103fa-47c5-cbe5-1775-e8eb5002bdfe</id>
<entry>
<title>IPv6 World Launch Day</title>
<link href="/blog/entries/ipv6-world-launch-day"></link>
<id>urn:uuid:5e0fcd22-cd65-97c3-767f-741c960041e9</id>
<updated>2012-04-08T15:36:00-07:00</updated>
<author><name>Barry Pederson</name>
<email>bp@barryp.org</email>
</author>
<content type="html">&lt;img src=&quot;/assets/7/18/World_IPv6_launch_banner_256.png&quot; alt=&quot;IPv6 Logo&quot; style=&quot;float:left; margin-right: 2em;&quot; width=&quot;106&quot; height=&quot;226&quot;/&gt;
&lt;p&gt;I&#039;ve been working on getting this website up and running under IPv6, and it turned out to be somewhat involved.
Firstly, I signed up with Hurricane Electric&#039;s &lt;a href=&quot;http://tunnelbroker.net&quot;&gt;tunnelbroker.net&lt;/a&gt;, to get IPv6 connectivity, because my ISP
doesn&#039;t offer it yet.  Setup my own DNS servers running &lt;a href=&quot;http://www.nlnetlabs.nl/projects/nsd/&quot;&gt;nsd&lt;/a&gt;, which was a bit of a learning curve, but in the long
run I think it&#039;ll be better than working with goofy DNS managers like you&#039;d find on registrar or hosting websites.&lt;br /&gt;
NameCheap is now letting you setup IPv6 glue records right on their website (previously you had to file a support
ticket), so that made things easier.&lt;/p&gt;
&lt;p&gt;The only big glitch I ran into is that on FreeBSD, using simply&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;listen [::]:80;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to listen to both IPv4 and IPv6 didn&#039;t work.  When trying that, I found that any request coming in as IPv4 would give
weird 403 or 404 (I don&#039;t remember which) errors, where it seemed nginx just didn&#039;t know what virtual host to go to.&lt;br /&gt;
Linux doesn&#039;t seem to have that problem. Ended up using separate listen statements, as in:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;listen 80 default_server;
listen [::]:80 default_server ipv6only=on;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;for the main site, but VERY IMPORTANTLY, the remaining sites could not have the &lt;code&gt;ipv6only=on&lt;/code&gt; directive, they just
simply say &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;listen  80;
listen [::]:80;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(found that trick in this &lt;a href=&quot;http://serverfault.com/questions/277653/nginx-name-based-virtual-hosts-on-ipv6&quot;&gt;ServerFault page&lt;/a&gt;).  This also has the advantage of showing proper IPv4 IP addresses in
the logs, instead of IPv4-mapped IPv6 addresses such as &lt;code&gt;::ffff:11.22.33.44&lt;/code&gt;, so I ended up doing the same thing on a
Linux box even though it handled dual-stack by default just fine.&lt;/p&gt;
&lt;p&gt;I also for testing purposes, made aliases&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://ipv4.barryp.org/blog/&quot;&gt;http://ipv4.barryp.org/blog/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ipv6.barryp.org/blog/&quot;&gt;http://ipv6.barryp.org/blog/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To force one protocol or the other.  When you use &lt;a href=&quot;http://barryp.org/blog/&quot;&gt;http://barryp.org/blog/&lt;/a&gt;, it&#039;s not obvious which you&#039;re
using.&lt;/p&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>
</feed>