diff options
context:
space:
mode:
authorBryce Kwon <bryce@brycekwon.com>
committerBryce Kwon <bryce@brycekwon.com>
commit
parent
tree
download
Make the server configs complete standalone files
Diffstat (limited to 'custom/servers')
-rw-r--r--custom/servers/apache.conf212
-rw-r--r--custom/servers/lighttpd.conf60
-rw-r--r--custom/servers/nginx.conf327
3 files changed, 395 insertions, 204 deletions
diff --git a/custom/servers/apache.conf b/custom/servers/apache.conf
index d042dd9..7ff3bab 100644
--- a/custom/servers/apache.conf
+++ b/custom/servers/apache.conf
@@ -1,9 +1,15 @@
# Apache httpd 2.4 configuration for cgit.
#
+# This is a complete httpd.conf rather than a vhost snippet, so nothing here
+# is included from elsewhere and no distro base config is assumed. Check it
+# and run it with
+# httpd -t -f /path/to/apache.conf # check the syntax
+# httpd -f /path/to/apache.conf # run it
+# To use it as an ordinary vhost file instead, drop everything above the
+# virtual hosts and let your distro's httpd.conf supply it.
+#
# Apache runs the cgit.cgi binary directly through mod_cgid, so no FastCGI
-# bridge is needed. Drop this file in your vhost directory, for example
-# /etc/apache2/sites-available/cgit.conf on Debian and Ubuntu or
-# /etc/httpd/conf.d/cgit.conf on RHEL and Fedora, then enable it and reload.
+# bridge is needed.
#
# Paths assumed below, edit them to match your install.
# cgit CGI binary /usr/lib/cgit/cgit.cgi
@@ -21,26 +27,134 @@
# routes from shadowing each other.
-# --- Required modules -------------------------------------------------------
+# ServerRoot is what every relative path below resolves against, including the
+# module paths. It is /etc/httpd on RHEL and Fedora and /etc/apache2 on Debian
+# and Ubuntu, where the modules live in /usr/lib/apache2/modules and the
+# LoadModule lines need that absolute path instead of the relative one.
+ServerRoot /etc/httpd
+PidFile /var/run/httpd.pid
+
+# Where Apache puts its runtime scratch, the mutexes and the SSL session
+# cache. It is /var/run/httpd on RHEL and Fedora and /var/run/apache2 on
+# Debian and Ubuntu. The directory has to exist and be writable before Apache
+# starts, which is normally the packaging's job.
+DefaultRuntimeDir /var/run/httpd
+
+Listen 80
+Listen 443
+
+# Set globally so Apache does not have to guess a name at startup, which it
+# warns about. Each vhost overrides it with its own.
+ServerName git.example.org
+
+# Drop the version number from the Server header and from error pages.
+ServerTokens Prod
+ServerSignature Off
+
+
# mod_cgid suits the threaded MPMs that ship by default. Use mod_cgi instead
-# only on the old prefork MPM. mod_alias provides Alias and ScriptAlias and
-# mod_env provides SetEnv. On Debian and Ubuntu run a2enmod cgid alias env
+# only on the old prefork MPM. mod_alias provides Alias and ScriptAlias,
+# mod_env provides SetEnv, and the rest are the core pieces a standalone
+# config cannot do without. On Debian and Ubuntu run
+# a2enmod cgid alias env headers expires ssl
# rather than editing these lines. The guards make double-loading harmless.
-<IfModule !mod_cgid.c>
- LoadModule cgid_module modules/mod_cgid.so
+<IfModule !mpm_event_module>
+ LoadModule mpm_event_module modules/mod_mpm_event.so
+</IfModule>
+<IfModule !unixd_module>
+ LoadModule unixd_module modules/mod_unixd.so
+</IfModule>
+<IfModule !authz_core_module>
+ LoadModule authz_core_module modules/mod_authz_core.so
</IfModule>
-<IfModule !mod_alias.c>
- LoadModule alias_module modules/mod_alias.so
+<IfModule !log_config_module>
+ LoadModule log_config_module modules/mod_log_config.so
</IfModule>
-<IfModule !mod_env.c>
- LoadModule env_module modules/mod_env.so
+<IfModule !mime_module>
+ LoadModule mime_module modules/mod_mime.so
</IfModule>
-<IfModule !mod_headers.c>
- LoadModule headers_module modules/mod_headers.so
+<IfModule !alias_module>
+ LoadModule alias_module modules/mod_alias.so
</IfModule>
+<IfModule !cgid_module>
+ LoadModule cgid_module modules/mod_cgid.so
+</IfModule>
+<IfModule !env_module>
+ LoadModule env_module modules/mod_env.so
+</IfModule>
+<IfModule !headers_module>
+ LoadModule headers_module modules/mod_headers.so
+</IfModule>
+<IfModule !expires_module>
+ LoadModule expires_module modules/mod_expires.so
+</IfModule>
+# TLS. Delete these two along with the HTTPS vhost to run plain HTTP only.
+# mod_socache_shmcb backs the SSL session cache and mod_ssl expects it.
+<IfModule !socache_shmcb_module>
+ LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
+</IfModule>
+<IfModule !ssl_module>
+ LoadModule ssl_module modules/mod_ssl.so
+</IfModule>
+
+
+# The user Apache drops to after binding the ports. It is apache on RHEL and
+# Fedora, www-data on Debian and Ubuntu, and http on Arch.
+User apache
+Group apache
+
+
+# The combined format comes from the distro config rather than from Apache
+# itself, so a standalone config has to define it before any CustomLog uses it.
+LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
+ErrorLog /var/log/apache2/error.log
+LogLevel warn
+
+
+# The usual "TypesConfig conf/mime.types" would pull in a second file. Exactly
+# five static files are served off disk, so their types are declared here
+# instead. Everything else Apache returns comes from cgit, which sets its own
+# Content-Type.
+AddType text/css .css
+AddType text/javascript .js
+AddType image/png .png
+AddType image/vnd.microsoft.icon .ico
+AddType text/plain .txt
+
+
+# Deny the whole filesystem, then open only the two directories cgit needs.
+# Without this a misplaced Alias could expose anything readable on the host.
+<Directory />
+ AllowOverride None
+ Require all denied
+</Directory>
+
+# The static asset directory, read only.
+<Directory "/usr/share/cgit">
+ Options None
+ AllowOverride None
+ Require all granted
+
+ # These assets rarely change, so let browsers cache them.
+ <IfModule mod_expires.c>
+ ExpiresActive On
+ ExpiresDefault "access plus 30 days"
+ </IfModule>
+</Directory>
+
+# The cgit binary.
+<Directory "/usr/lib/cgit">
+ # Allow CGI execution here. ScriptAlias implies it, stating it makes the
+ # intent clear.
+ Options +ExecCGI
+ # Run cgit.cgi as a CGI even if it is ever reached through a plain Alias
+ # rather than ScriptAlias.
+ SetHandler cgi-script
+ AllowOverride None
+ Require all granted
+</Directory>
-# --- Plain HTTP virtual host ------------------------------------------------
# This vhost only bounces plain HTTP up to HTTPS. It serves no cgit itself,
# every cgit directive lives in the HTTPS vhost below. To run without TLS for
# now, convert the HTTPS vhost to port 80 and delete this whole block rather
@@ -56,39 +170,44 @@
</VirtualHost>
-# --- HTTPS virtual host, this one serves cgit -------------------------------
-# To run without TLS for now, change this opening line to <VirtualHost *:80>,
-# delete the three SSL lines, and delete the port 80 vhost above so there is
-# only one vhost. Everything else stays the same.
+# The vhost that serves cgit. To run without TLS for now, change this opening
+# line to port 80, delete the SSL lines, and delete the vhost above so there
+# is only one. Everything else stays as it is.
<VirtualHost *:443>
ServerName git.example.org
ErrorLog /var/log/apache2/cgit_ssl_error.log
CustomLog /var/log/apache2/cgit_ssl_access.log combined
- # TLS needs mod_ssl (a2enmod ssl). Point these at your certificate.
+ # Point these at your certificate.
SSLEngine on
SSLCertificateFile /etc/ssl/certs/git.example.org.crt
SSLCertificateKeyFile /etc/ssl/private/git.example.org.key
+ # Subtractive rather than naming the versions to keep, since a mod_ssl
+ # built before TLS 1.3 rejects the +TLSv1.3 token outright and refuses to
+ # start. This form enables 1.3 wherever it exists.
+ SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
# Which config cgit reads. It falls back to the compiled-in /etc/cgitrc,
# the same path used here, but setting it makes the location explicit and
# lets you point at a per-vhost file later.
SetEnv CGIT_CONFIG /etc/cgitrc
- # --- Security headers (needs mod_headers, a2enmod headers) --------------
- # Set here, not in cgit, so they also cover the static assets Apache
- # serves. script-src stays self because cgit loads only its own cgit.js,
- # and style-src allows inline for the diffstat bars. If you enable the
- # gravatar or libravatar avatar filter, add its host to img-src, for
- # example https://www.gravatar.com.
+ # The only request body cgit ever reads is the auth-filter login form, and
+ # it stops after 4096 bytes. Nothing else here accepts an upload.
+ LimitRequestBody 65536
+
+ # Security headers are set here, not in cgit, so they also cover the static
+ # assets Apache serves. script-src stays self because cgit loads only its
+ # own cgit.js, and style-src allows inline for the diffstat bars. If you
+ # enable the gravatar or libravatar avatar filter, add its host to img-src,
+ # for example https://www.gravatar.com.
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'self'"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "no-referrer"
# Enable only once you serve HTTPS exclusively, since it is hard to undo.
#Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
- # --- Static assets, served directly by Apache ---------------------------
# These five files are the only things served off disk. Each Alias maps
# one URL to one file. Because they come before the ScriptAlias below, a
# request for /cgit.css is answered from disk and never reaches cgit.
@@ -101,22 +220,6 @@
Alias /favicon.ico /usr/share/cgit/favicon.ico
Alias /robots.txt /usr/share/cgit/robots.txt
- # Apache 2.4 denies filesystem access by default, so open the asset
- # directory for reading.
- <Directory "/usr/share/cgit">
- Options None
- AllowOverride None
- Require all granted
-
- # Optional. These assets rarely change, so let browsers cache them.
- # Needs mod_expires (a2enmod expires). Safe to delete this block.
- <IfModule mod_expires.c>
- ExpiresActive On
- ExpiresDefault "access plus 30 days"
- </IfModule>
- </Directory>
-
- # --- cgit, the catch-all ------------------------------------------------
# ScriptAlias maps a URL prefix to a path, marks it executable, and
# forwards the rest of the URL as PATH_INFO. Mapping / makes cgit the
# handler for every URL the static Aliases above did not already claim.
@@ -128,21 +231,20 @@
# h=next. cgit derives its link base from SCRIPT_NAME, which at the domain
# root is / and needs no tuning. For a sub-path install see the note below.
ScriptAlias / /usr/lib/cgit/cgit.cgi/
-
- <Directory "/usr/lib/cgit">
- # Allow CGI execution here. ScriptAlias implies it, stating it makes
- # the intent clear.
- Options +ExecCGI
- # Run cgit.cgi as a CGI even if it is ever reached through a plain
- # Alias rather than ScriptAlias.
- SetHandler cgi-script
- AllowOverride None
- Require all granted
- </Directory>
</VirtualHost>
-# --- Sub-path install, only if cgit is not at the domain root ---------------
+# The SSL session cache is a global mod_ssl setting, so it sits outside the
+# vhosts. Resumption keeps a browser paging through a repository from redoing
+# a full handshake on every connection. Delete along with the HTTPS vhost if
+# you serve plain HTTP.
+<IfModule mod_ssl.c>
+ # Relative, so it lands in DefaultRuntimeDir and follows it across distros.
+ SSLSessionCache "shmcb:ssl_scache(512000)"
+ SSLSessionCacheTimeout 300
+</IfModule>
+
+
# To serve cgit at https://git.example.org/cgit/ instead of the root, change
# the ScriptAlias to
# ScriptAlias /cgit/ /usr/lib/cgit/cgit.cgi/
diff --git a/custom/servers/lighttpd.conf b/custom/servers/lighttpd.conf
index 3111ed0..6ae87e0 100644
--- a/custom/servers/lighttpd.conf
+++ b/custom/servers/lighttpd.conf
@@ -1,5 +1,11 @@
# lighttpd configuration for cgit.
#
+# This is a complete lighttpd.conf rather than a snippet for conf-enabled, so
+# nothing here is included from elsewhere and no distro base config is
+# assumed. Check it and run it with
+# lighttpd -tt -f /path/to/lighttpd.conf # check the syntax and modules
+# lighttpd -D -f /path/to/lighttpd.conf # run it in the foreground
+#
# lighttpd runs the cgit.cgi binary directly through mod_cgi, so no FastCGI
# bridge is needed. This is cgit's classic reference deployment, mod_cgi with
# mod_alias and mod_setenv.
@@ -16,29 +22,40 @@
# straight off disk and must never be routed through cgit.
-# --- Modules ----------------------------------------------------------------
-# Append the three modules cgit needs so the distro's base config is kept.
-# mod_alias maps URL paths onto files, mod_setenv injects CGIT_CONFIG, and
-# mod_cgi runs cgit.cgi.
-server.modules += ( "mod_alias", "mod_setenv", "mod_cgi" )
+# A plain assignment rather than "+=", since this config stands on its own and
+# there is no distro base list to append to. mod_alias maps URL paths onto
+# files, mod_setenv injects CGIT_CONFIG and the response headers, and mod_cgi
+# runs cgit.cgi. Adding mod_accesslog here is what the access log below needs.
+server.modules = (
+ "mod_alias",
+ "mod_setenv",
+ "mod_cgi",
+ "mod_accesslog",
+)
-# --- Server basics ----------------------------------------------------------
server.port = 80
server.username = "http" # Debian and Ubuntu use www-data
server.groupname = "http"
server.document-root = "/usr/share/cgit" # a valid docroot must exist, the
# alias rules below do the routing
+server.pid-file = "/run/lighttpd.pid"
server.errorlog = "/var/log/lighttpd/error.log"
-# Access logging needs mod_accesslog. Load it and uncomment to enable.
-#server.modules += ( "mod_accesslog" )
-#accesslog.filename = "/var/log/lighttpd/access.log"
+accesslog.filename = "/var/log/lighttpd/access.log"
+
+# Drop the version number from the Server header and from error pages.
+server.tag = "lighttpd"
+
+# The only request body cgit ever reads is the auth-filter login form, and it
+# stops after 4096 bytes. Nothing else here accepts an upload. The value is in
+# kilobytes and the default of 0 means unlimited.
+server.max-request-size = 64
-# --- MIME types for the static assets ---------------------------------------
# mod_alias serves the assets off disk, so lighttpd must know their content
# types. Without this the stylesheet is sent as application/octet-stream and
-# the browser ignores it.
+# the browser ignores it. Only these five files are served off disk, so this
+# short table is the whole of it and no external mime file is needed.
mimetype.assign = (
".css" => "text/css",
".js" => "text/javascript",
@@ -48,20 +65,19 @@ mimetype.assign = (
)
-# --- Virtual host, git.example.org ------------------------------------------
-# A top-level conditional, so it matches on both the port 80 socket and the
-# optional TLS socket at the end of this file.
+# The vhost, as a top-level conditional so it matches on both the port 80
+# socket and the optional TLS socket at the end of this file.
$HTTP["host"] == "git.example.org" {
# Which config cgit reads. It falls back to the compiled-in /etc/cgitrc,
# the same path used here, but setting it makes the location explicit.
setenv.add-environment = ( "CGIT_CONFIG" => "/etc/cgitrc" )
- # --- Security headers ---------------------------------------------------
- # Set here, not in cgit, so they also cover the static assets lighttpd
- # serves. script-src stays self because cgit loads only its own cgit.js,
- # and style-src allows inline for the diffstat bars. If you enable the
- # gravatar or libravatar avatar filter, add its host to img-src.
+ # Security headers are set here, not in cgit, so they also cover the
+ # static assets lighttpd serves. script-src stays self because cgit loads
+ # only its own cgit.js, and style-src allows inline for the diffstat bars.
+ # If you enable the gravatar or libravatar avatar filter, add its host to
+ # img-src.
setenv.add-response-header = (
"Content-Security-Policy" => "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'self'",
"X-Content-Type-Options" => "nosniff",
@@ -109,9 +125,9 @@ $HTTP["host"] == "git.example.org" {
}
-# --- Optional HTTPS on 443 --------------------------------------------------
-# Uncomment this whole block to enable TLS. The host block above is socket
-# independent, so it serves cgit over this socket too once the crypto is set.
+# Uncomment this whole block to enable TLS on 443. The host block above is
+# socket independent, so it serves cgit over this socket too once the crypto
+# is set.
#server.modules += ( "mod_openssl" )
#
#$SERVER["socket"] == ":443" {
diff --git a/custom/servers/nginx.conf b/custom/servers/nginx.conf
index 76ac260..3b0b7ef 100644
--- a/custom/servers/nginx.conf
+++ b/custom/servers/nginx.conf
@@ -1,8 +1,10 @@
# nginx configuration for cgit.
#
-# nginx cannot run CGI programs itself, so a small bridge called fcgiwrap
-# runs the cgit.cgi binary and speaks FastCGI to nginx. Starting fcgiwrap is
-# covered in the notes at the end of this file.
+# This is a complete nginx.conf rather than a snippet for conf.d or
+# sites-enabled, so nothing here is included from elsewhere. Install it and
+# reload, or point nginx straight at it to try it out.
+# nginx -t -c /path/to/nginx.conf # check the syntax
+# nginx -c /path/to/nginx.conf # run it
#
# Paths assumed below, edit them to match your install.
# cgit CGI binary /usr/lib/cgit/cgit.cgi
@@ -16,166 +18,237 @@
# base from SCRIPT_NAME. The five static assets are served straight off disk
# and must never be routed through cgit. Passing PATH_INFO through is the
# single most important part of the config below.
+#
+# nginx cannot run CGI programs itself, so a small bridge called fcgiwrap runs
+# the cgit.cgi binary and speaks FastCGI to nginx. Nothing below works until
+# that socket exists. On Debian and Ubuntu the packaged systemd socket
+# provides /run/fcgiwrap.socket, so enabling it is enough.
+# apt install fcgiwrap
+# systemctl enable --now fcgiwrap.socket
+# The socket must be readable by nginx's user. The packaged unit runs fcgiwrap
+# as www-data, which nginx also uses on those systems. Without systemd you can
+# run
+# spawn-fcgi -s /run/fcgiwrap.socket -M 660 -- /usr/sbin/fcgiwrap
+# or run fcgiwrap over TCP and point fastcgi_pass at 127.0.0.1:9000.
-# --- Optional HTTP to HTTPS redirect ----------------------------------------
-# Delete this whole server block if you serve plain HTTP only.
-server {
- listen 80;
- listen [::]:80;
- server_name git.example.org;
+# The user nginx drops to after binding the ports. It is www-data on Debian
+# and Ubuntu, nginx on RHEL and Fedora, and http on Arch and Alpine. It has to
+# match whatever owns the fcgiwrap socket.
+user www-data;
+worker_processes auto;
+pid /run/nginx.pid;
- # ACME http-01 challenge files, if you use certbot in webroot mode.
- location ^~ /.well-known/acme-challenge/ {
- root /var/www/html;
- }
+# Startup and worker errors. Per-site request logs are set in the vhost.
+error_log /var/log/nginx/error.log warn;
- # Everything else moves to HTTPS.
- location / {
- return 301 https://$host$request_uri;
- }
+events {
+ worker_connections 1024;
}
-# --- Main site --------------------------------------------------------------
-# Written for TLS on 443. For a quick plain-HTTP test, change the two listen
-# lines to port 80, delete the redirect block above, and delete the four ssl
-# lines below. Everything else stays the same.
-server {
- listen 443 ssl;
- listen [::]:443 ssl;
- http2 on;
- server_name git.example.org;
+http {
+ # nginx's compiled-in type table knows only text/html, and the usual
+ # "include mime.types" would pull in a second file. Exactly five static
+ # files are served off disk, so their types are declared here instead and
+ # this config keeps standing on its own. Everything else nginx returns
+ # comes from cgit, which sets its own Content-Type.
+ types {
+ text/css css;
+ text/javascript js;
+ image/png png;
+ image/vnd.microsoft.icon ico;
+ text/plain txt;
+ }
+ default_type application/octet-stream;
+
+ sendfile on;
+ tcp_nopush on;
+ keepalive_timeout 65;
- ssl_certificate /etc/letsencrypt/live/git.example.org/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/git.example.org/privkey.pem;
- ssl_protocols TLSv1.2 TLSv1.3;
- ssl_ciphers HIGH:!aNULL:!MD5;
+ # Drop the version number from the Server header and from error pages.
+ server_tokens off;
- # --- Security headers ---------------------------------------------------
- # These sit here, not in cgit, because they must also cover the static
- # assets nginx serves directly. cgit loads only its own /cgit.js and uses
- # inline style on the diffstat bars, so script-src stays self while
- # style-src allows inline. always applies them to error responses too. If
- # you enable the gravatar or libravatar avatar filter, add its host to
- # img-src, for example https://www.gravatar.com or https://seccdn.libravatar.org.
- add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'self'" always;
- add_header X-Content-Type-Options "nosniff" always;
- add_header Referrer-Policy "no-referrer" always;
- # Enable only once you serve HTTPS exclusively, since it is hard to undo.
- #add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
+ # cgit pages are large and highly compressible, so this is the cheapest
+ # speedup available. text/html is always compressed and cannot be listed.
+ # Snapshot tarballs are deliberately absent, since they arrive compressed
+ # already and running them through gzip again only burns CPU.
+ gzip on;
+ gzip_vary on;
+ gzip_proxied any;
+ gzip_min_length 1024;
+ gzip_types text/css text/javascript text/plain application/atom+xml;
- # The document root is the directory that holds the static assets. cgit
- # emits absolute links to /cgit.css and /cgit.png by default, so those
- # files must resolve at the root of the URL space. Pointing root at the
- # asset directory makes /cgit.css map to /usr/share/cgit/cgit.css.
- root /usr/share/cgit;
- # Upload cap for large form posts. Snapshots are generated rather than
- # uploaded, so this does not limit them.
- client_max_body_size 64m;
+ # Bounce plain HTTP up to HTTPS. Delete this whole server block if you
+ # serve plain HTTP only.
+ server {
+ listen 80;
+ listen [::]:80;
+ server_name git.example.org;
- access_log /var/log/nginx/cgit.access.log;
- error_log /var/log/nginx/cgit.error.log;
+ # ACME http-01 challenge files, if you use certbot in webroot mode.
+ location ^~ /.well-known/acme-challenge/ {
+ root /var/www/html;
+ }
- # --- Static assets, served directly -------------------------------------
- # Match the assets by their exact root-level names, never by bare
- # extension. cgit routes on PATH_INFO and a repository can hold files
- # ending in .css or .png, so /myrepo/tree/style.css and /myrepo/plain/
- # logo.png are real cgit URLs. A broad extension match would capture
- # those, look for them on disk, and return 404 before cgit could render
- # them. Anchoring the regex at the start of the path matches /cgit.css but
- # not /myrepo/tree/cgit.css, so it can never shadow a repository file. An
- # nginx regex location is matched before the prefix location below, so
- # these assets win for their exact URLs and cgit wins for the rest.
- location ~ ^/(cgit\.css|cgit\.js|cgit\.png|favicon\.ico|robots\.txt)$ {
- expires 30d;
- access_log off;
- try_files $uri =404;
+ # Everything else moves to HTTPS.
+ location / {
+ return 301 https://$host$request_uri;
+ }
}
- # --- cgit, the catch-all ------------------------------------------------
- # Everything that is not a static asset above is a cgit URL, the repo
- # index, a repository, a page within a repository, a snapshot, a feed.
- location / {
- # nginx's standard FastCGI parameters, some of which are overridden
- # below. A later fastcgi_param wins, so include order does not matter.
- include fastcgi_params;
- # The program fcgiwrap runs. It must be the cgit binary itself, not
- # $document_root$fastcgi_script_name, which would try to run a repo
- # path and is the usual cause of a failed request.
- fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi;
+ # The site itself, written for TLS on 443. For a quick plain-HTTP test,
+ # change the two listen lines to port 80, delete the redirect block above,
+ # and delete the http2 and ssl lines below. Everything else stays as it is.
+ server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+ # nginx 1.25.1 and newer. On older builds delete this and write the
+ # listen lines as "listen 443 ssl http2;" instead.
+ http2 on;
+ server_name git.example.org;
- # cgit builds its link base, the virtual root, from SCRIPT_NAME. The
- # stock parameters set SCRIPT_NAME to the whole request path, which
- # would make cgit prepend that path to every link. Served at the
- # domain root the script has no prefix, so force SCRIPT_NAME empty and
- # cgit uses / as its base. A sub-path install sets it instead, see the
- # end of this file.
- fastcgi_param SCRIPT_NAME "";
+ ssl_certificate /etc/letsencrypt/live/git.example.org/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/git.example.org/privkey.pem;
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_ciphers HIGH:!aNULL:!MD5;
+ # Let the client pick, which is the modern advice once the ancient
+ # protocol versions are already excluded above.
+ ssl_prefer_server_ciphers off;
+ # Resumption, so a browser paging through a repository is not made to
+ # redo a full handshake on every connection.
+ ssl_session_cache shared:SSL:10m;
+ ssl_session_timeout 1d;
+ ssl_session_tickets off;
- # How cgit learns the repository and page. At the domain root the
- # whole request path is the PATH_INFO.
- fastcgi_param PATH_INFO $uri;
+ # Security headers sit here, not in cgit, because they must also cover
+ # the static assets nginx serves directly. cgit loads only its own
+ # /cgit.js and uses inline style on the diffstat bars, so script-src
+ # stays self while style-src allows inline. always applies them to
+ # error responses too. If you enable the gravatar or libravatar avatar
+ # filter, add its host to img-src, for example https://www.gravatar.com
+ # or https://seccdn.libravatar.org.
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'self'" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header Referrer-Policy "no-referrer" always;
+ # Enable only once you serve HTTPS exclusively, since it is hard to undo.
+ #add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
- # Page options such as h=branch, id=sha and the snapshot format.
- fastcgi_param QUERY_STRING $query_string;
+ # The document root is the directory that holds the static assets. cgit
+ # emits absolute links to /cgit.css and /cgit.png by default, so those
+ # files must resolve at the root of the URL space. Pointing root at the
+ # asset directory makes /cgit.css map to /usr/share/cgit/cgit.css.
+ root /usr/share/cgit;
- # Where the static assets live, kept consistent with root above.
- fastcgi_param DOCUMENT_ROOT $document_root;
+ # The only request body cgit ever reads is the auth-filter login form,
+ # and it stops after 4096 bytes. Nothing else here accepts an upload,
+ # so keep the cap far below the nginx default of 1m.
+ client_max_body_size 64k;
- # The browser's Host header, so cgit builds clone URLs against the
- # name the visitor used rather than server_name.
- fastcgi_param HTTP_HOST $http_host;
+ access_log /var/log/nginx/cgit.access.log combined;
+ error_log /var/log/nginx/cgit.error.log;
- # Which config cgit reads. It checks CGIT_CONFIG and falls back to the
- # compiled-in /etc/cgitrc. Setting it makes the location explicit and
- # lets you move cgitrc without recompiling.
- fastcgi_param CGIT_CONFIG /etc/cgitrc;
+ # Match the assets by their exact root-level names, never by bare
+ # extension. cgit routes on PATH_INFO and a repository can hold files
+ # ending in .css or .png, so /myrepo/tree/style.css and /myrepo/plain/
+ # logo.png are real cgit URLs. A broad extension match would capture
+ # those, look for them on disk, and return 404 before cgit could render
+ # them. Anchoring the regex at the start of the path matches /cgit.css
+ # but not /myrepo/tree/cgit.css, so it can never shadow a repository
+ # file. An nginx regex location is matched before the prefix location
+ # below, so these assets win for their exact URLs and cgit wins for the
+ # rest.
+ location ~ ^/(cgit\.css|cgit\.js|cgit\.png|favicon\.ico|robots\.txt)$ {
+ expires 30d;
+ access_log off;
+ try_files $uri =404;
+ }
- # The real scheme, so cgit builds correct https clone URLs.
- fastcgi_param HTTPS $https if_not_empty;
+ # Everything that is not a static asset above is a cgit URL, the repo
+ # index, a repository, a page within a repository, a snapshot, a feed.
+ location / {
+ # The CGI environment. This is normally "include fastcgi_params",
+ # which would be a second file, so the list is written out here.
+ # Of all of it cgit itself reads only CGIT_CONFIG, PATH_INFO,
+ # QUERY_STRING, SCRIPT_NAME, REQUEST_METHOD, CONTENT_LENGTH,
+ # HTTP_HOST, HTTPS, SERVER_NAME, SERVER_PORT, HTTP_COOKIE and
+ # HTTP_REFERER. The cookie and referer arrive on their own, since
+ # nginx forwards request headers as HTTP_* without being asked.
- # Hand off to the fcgiwrap socket. See the notes for how to create it.
- # A TCP fcgiwrap would use for example 127.0.0.1:9000 here.
- fastcgi_pass unix:/run/fcgiwrap.socket;
+ # The program fcgiwrap runs. It must be the cgit binary itself, not
+ # $document_root$fastcgi_script_name, which would try to run a repo
+ # path and is the usual cause of a failed request.
+ fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi;
- # Large outputs such as snapshot tarballs and blame on big files can
- # take a while, so give cgit room and stream rather than buffer.
- fastcgi_read_timeout 300s;
- fastcgi_buffering off;
- }
-}
+ # cgit builds its link base, the virtual root, from SCRIPT_NAME.
+ # The stock parameters set SCRIPT_NAME to the whole request path,
+ # which would make cgit prepend that path to every link. Served at
+ # the domain root the script has no prefix, so force SCRIPT_NAME
+ # empty and cgit uses / as its base. A sub-path install sets it
+ # instead, see the end of this file.
+ fastcgi_param SCRIPT_NAME "";
+ # How cgit learns the repository and page. At the domain root the
+ # whole request path is the PATH_INFO.
+ fastcgi_param PATH_INFO $uri;
-# --- Notes, starting fcgiwrap -----------------------------------------------
-# cgit is a CGI binary and fcgiwrap is the CGI to FastCGI bridge nginx talks
-# to. On Debian and Ubuntu the packaged systemd socket provides
-# /run/fcgiwrap.socket, so enabling it is enough.
-# apt install fcgiwrap
-# systemctl enable --now fcgiwrap.socket
-# The socket must be readable by nginx's user. The packaged unit runs fcgiwrap
-# as www-data, which nginx also uses on those systems. Without systemd you can
-# run
-# spawn-fcgi -s /run/fcgiwrap.socket -M 660 -- /usr/sbin/fcgiwrap
-# or run fcgiwrap over TCP and point fastcgi_pass at 127.0.0.1:9000.
+ # Page options such as h=branch, id=sha and the snapshot format.
+ fastcgi_param QUERY_STRING $query_string;
+
+ # Which config cgit reads. It checks CGIT_CONFIG and falls back to
+ # the compiled-in /etc/cgitrc. Setting it makes the location
+ # explicit and lets you move cgitrc without recompiling.
+ fastcgi_param CGIT_CONFIG /etc/cgitrc;
+
+ # The browser's Host header, so cgit builds clone URLs against the
+ # name the visitor used rather than server_name.
+ fastcgi_param HTTP_HOST $http_host;
+
+ # The real scheme, so cgit builds correct https clone URLs.
+ fastcgi_param HTTPS $https if_not_empty;
+
+ # The routine remainder, needed by fcgiwrap and by the login form.
+ fastcgi_param REQUEST_METHOD $request_method;
+ fastcgi_param CONTENT_TYPE $content_type;
+ fastcgi_param CONTENT_LENGTH $content_length;
+ 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 REQUEST_SCHEME $scheme;
+ 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;
+
+ # Hand off to the fcgiwrap socket. A TCP fcgiwrap would use for
+ # example 127.0.0.1:9000 here.
+ fastcgi_pass unix:/run/fcgiwrap.socket;
+
+ # Large outputs such as snapshot tarballs and blame on big files
+ # can take a while, so give cgit room and stream rather than buffer.
+ fastcgi_read_timeout 300s;
+ fastcgi_buffering off;
+ }
+ }
+}
-# --- Alternative, serving cgit under a sub-path -----------------------------
# To serve cgit at https://git.example.org/cgit/ instead of the root, split
-# the URL so SCRIPT_NAME is the prefix and PATH_INFO is the rest.
+# the URL so SCRIPT_NAME is the prefix and PATH_INFO is the rest. Keep every
+# other fastcgi_param from the location above.
#
# location /cgit/ {
-# include fastcgi_params;
# fastcgi_split_path_info ^(/cgit)(/.*)$;
# fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi;
# fastcgi_param SCRIPT_NAME $fastcgi_script_name;
# fastcgi_param PATH_INFO $fastcgi_path_info;
-# fastcgi_param QUERY_STRING $query_string;
-# fastcgi_param HTTP_HOST $http_host;
-# fastcgi_param CGIT_CONFIG /etc/cgitrc;
-# fastcgi_param HTTPS $https if_not_empty;
+# ...
# fastcgi_pass unix:/run/fcgiwrap.socket;
# }
#