Define .htaccess files declaratively

Here is the solution I came up with:

  services.httpd.enable = true;
  services.httpd.adminAddr = "webmaster@example.org";
  services.httpd.virtualHosts.localhost = {
    extraConfig = ''
      ScriptAlias /cgi-bin/ /var/cgi/scripts/

      <Directory /var/cgi/scripts>
        DirectoryIndex index.pl
        Require all granted
      </Directory>
    '';
  };

If you want a perfectly (one time) reproducible solution you can test in a NixOS VM take the above and add this very dirty hack:

  systemd.tmpfiles.rules =
    let
      script = builtins.replaceStrings [ "\n" "\"" "\\" ] [ "\\n" "\\\"" "\\\\" ] ''
        #! ${pkgs.perl}/bin/perl
        print "Content-type: text/html\n\n";
        print "<html><body><h1>Hello World!";
        print "</h1></body></html>\n";
      '';
    in
      [
        "d /var/cgi"
        "d /var/cgi/scripts"
        "f /var/cgi/scripts/index.pl 0755 - - - ${script}"
      ];

From here:

~> curl localhost:80/cgi-bin/                                                                                                                                                   
<html><body><h1>Hello World!</h1></body></html>

In summary:

  • make sure index.pl (or whatever your cgi program is) has executable bit set
  • make sure your scripts directory has an apache <Directory> directive with Require all granted (or equivalent, depending on your needs)
  • under no situation ever should you or anyone use the systemd.tmpfiles.rules hack I have written here, other than for a disposable VM test if the apache configuration alone I provided doesn’t work

Let me know if you have any issues.

1 Like