diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..aed6467
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,8 @@
+{
+  "semi": true,
+  "trailingComma": "all",
+  "singleQuote": true,
+  "printWidth": 120,
+  "tabWidth": 2,
+  "endOfLine": "lf"
+}
diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml
index 00097b2..a5ff820 100644
--- a/.woodpecker/build.yml
+++ b/.woodpecker/build.yml
@@ -48,7 +48,7 @@ steps:
       - go version
       - just build-tag ${CI_COMMIT_TAG##v}
     when:
-      - event: ["tag"]
+      - event: ['tag']
         branch:
           - ${CI_REPO_DEFAULT_BRANCH}
 
@@ -79,7 +79,7 @@ steps:
 
   release:
     depends_on: build
-    image: plugins/gitea-release:0.3.1
+    image: plugins/gitea-release:1.1.0
     settings:
       base_url: https://codeberg.org
       file_exists: overwrite
@@ -92,7 +92,7 @@ steps:
       - CI_BUILD_EVENT=${CI_BUILD_EVENT}
       - CI_COMMIT_REF=${CI_COMMIT_REF}
     when:
-      - event: ["tag"]
+      - event: ['tag']
         branch:
           - ${CI_REPO_DEFAULT_BRANCH}
 
@@ -109,7 +109,7 @@ steps:
       password:
         from_secret: bot_token
     when:
-      - event: ["push"]
+      - event: ['push']
         branch: ${CI_REPO_DEFAULT_BRANCH}
 
   docker-tag:
@@ -119,11 +119,11 @@ steps:
       registry: codeberg.org
       dockerfile: Dockerfile
       repo: codeberg.org/codeberg/pages-server
-      tags: [latest, "${CI_COMMIT_TAG}"]
+      tags: [latest, '${CI_COMMIT_TAG}']
       username:
         from_secret: bot_user
       password:
         from_secret: bot_token
     when:
-      - event: ["push"]
+      - event: ['push']
         branch: ${CI_REPO_DEFAULT_BRANCH}
diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml
index cd62359..8a90a15 100644
--- a/.woodpecker/lint.yml
+++ b/.woodpecker/lint.yml
@@ -34,3 +34,9 @@ steps:
       - event: pull_request
       - event: push
         branch: renovate/*
+
+  prettier:
+    image: docker.io/woodpeckerci/plugin-prettier:0.1.0
+    depends_on: []
+    settings:
+      version: 3.2.5
diff --git a/FEATURES.md b/FEATURES.md
index 3d2f394..52b90e5 100644
--- a/FEATURES.md
+++ b/FEATURES.md
@@ -19,16 +19,16 @@ Redirects can be created with a `_redirects` file with the following format:
 from  to  [status]
 ```
 
-* Lines starting with `#` are ignored
-* `from` - the path to redirect from (Note: repository and branch names are removed from request URLs)
-* `to` - the path or URL to redirect to
-* `status` - status code to use when redirecting (default 301)
+- Lines starting with `#` are ignored
+- `from` - the path to redirect from (Note: repository and branch names are removed from request URLs)
+- `to` - the path or URL to redirect to
+- `status` - status code to use when redirecting (default 301)
 
 ### Status codes
 
-* `200` - returns content from specified path (no external URLs) without changing the URL (rewrite)
-* `301` - Moved Permanently (Permanent redirect)
-* `302` - Found (Temporary redirect)
+- `200` - returns content from specified path (no external URLs) without changing the URL (rewrite)
+- `301` - Moved Permanently (Permanent redirect)
+- `302` - Found (Temporary redirect)
 
 ### Examples
 
diff --git a/README.md b/README.md
index f47a196..c23d86a 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 [![License: EUPL-1.2](https://img.shields.io/badge/License-EUPL--1.2-blue)](https://opensource.org/license/eupl-1-2/)
 [![status-badge](https://ci.codeberg.org/api/badges/Codeberg/pages-server/status.svg)](https://ci.codeberg.org/Codeberg/pages-server)
 <a href="https://matrix.to/#/#gitea-pages-server:matrix.org" title="Join the Matrix room at https://matrix.to/#/#gitea-pages-server:matrix.org">
-  <img src="https://img.shields.io/matrix/gitea-pages-server:matrix.org?label=matrix">
+<img src="https://img.shields.io/matrix/gitea-pages-server:matrix.org?label=matrix">
 </a>
 
 Gitea lacks the ability to host static pages from Git.
@@ -21,19 +21,19 @@ and the [Codeberg Documentation](https://docs.codeberg.org/codeberg-pages/).
 This is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories.
 Mapping custom domains is not static anymore, but can be done with DNS:
 
-1) add a `.domains` text file to your repository, containing the allowed domains, separated by new lines. The
-first line will be the canonical domain/URL; all other occurrences will be redirected to it.
+1. add a `.domains` text file to your repository, containing the allowed domains, separated by new lines. The
+   first line will be the canonical domain/URL; all other occurrences will be redirected to it.
 
-2) add a CNAME entry to your domain, pointing to `[[{branch}.]{repo}.]{owner}.codeberg.page` (repo defaults to
-"pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else.
-If the branch name contains slash characters, you need to replace "/" in the branch name to "~"):
-  `www.example.org. IN CNAME main.pages.example.codeberg.page.`
+2. add a CNAME entry to your domain, pointing to `[[{branch}.]{repo}.]{owner}.codeberg.page` (repo defaults to
+   "pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else.
+   If the branch name contains slash characters, you need to replace "/" in the branch name to "~"):
+   `www.example.org. IN CNAME main.pages.example.codeberg.page.`
 
-3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record
-for "example.org" (if your provider allows ALIAS or similar records, otherwise use A/AAAA), together with a TXT
-record that points to your repo (just like the CNAME record):
-  `example.org IN ALIAS codeberg.page.`
-  `example.org IN TXT main.pages.example.codeberg.page.`
+3. if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record
+   for "example.org" (if your provider allows ALIAS or similar records, otherwise use A/AAAA), together with a TXT
+   record that points to your repo (just like the CNAME record):
+   `example.org IN ALIAS codeberg.page.`
+   `example.org IN TXT main.pages.example.codeberg.page.`
 
 Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge.
 
@@ -43,7 +43,8 @@ Certificates are generated, updated and cleaned up automatically via Let's Encry
 
 ## Deployment
 
-**Warning: Some Caveats Apply**  
+**Warning: Some Caveats Apply**
+
 > Currently, the deployment requires you to have some knowledge of system administration as well as understanding and building code,
 > so you can eventually edit non-configurable and codeberg-specific settings.
 > In the future, we'll try to reduce these and make hosting Codeberg Pages as easy as setting up Gitea.
@@ -74,7 +75,7 @@ and especially have a look at [this section of the haproxy.cfg](https://codeberg
 - `ACME_API` (default: <https://acme-v02.api.letsencrypt.org/directory>): set this to <https://acme.mock.director> to use invalid certificates without any verification (great for debugging).  
   ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet.
 - `ACME_EMAIL` (default: `noreply@example.email`): Set the email sent to the ACME API server to receive, for example, renewal reminders.
-- `ACME_EAB_KID` &  `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL.
+- `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL.
 - `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider.
 - `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL.
 - `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80.
diff --git a/examples/haproxy-sni/README.md b/examples/haproxy-sni/README.md
index 8bb8f02..8165d6c 100644
--- a/examples/haproxy-sni/README.md
+++ b/examples/haproxy-sni/README.md
@@ -1,8 +1,9 @@
 # HAProxy with SNI & Host-based rules
 
-This is a proof of concept, enabling HAProxy to use *either* SNI to redirect to backends with their own HTTPS certificates (which are then fully exposed to the client; HAProxy only proxies on a TCP level in that case), *as well as* to terminate HTTPS and use the Host header to redirect to backends that use HTTP (or a new HTTPS connection).
+This is a proof of concept, enabling HAProxy to use _either_ SNI to redirect to backends with their own HTTPS certificates (which are then fully exposed to the client; HAProxy only proxies on a TCP level in that case), _as well as_ to terminate HTTPS and use the Host header to redirect to backends that use HTTP (or a new HTTPS connection).
 
 ## How it works
+
 1. The `http_redirect_frontend` is only there to listen on port 80 and redirect every request to HTTPS.
 2. The `https_sni_frontend` listens on port 443 and chooses a backend based on the SNI hostname of the TLS connection.
 3. The `https_termination_backend` passes all requests to a unix socket (using the plain TCP data).
@@ -11,6 +12,7 @@ This is a proof of concept, enabling HAProxy to use *either* SNI to redirect to
 In the example (see [haproxy.cfg](haproxy.cfg)), the `pages_backend` is listening via HTTPS and is providing its own HTTPS certificates, while the `gitea_backend` only provides HTTP.
 
 ## How to test
+
 ```bash
 docker-compose up &
 ./test.sh
diff --git a/examples/haproxy-sni/docker-compose.yml b/examples/haproxy-sni/docker-compose.yml
index 486264f..2cac2ad 100644
--- a/examples/haproxy-sni/docker-compose.yml
+++ b/examples/haproxy-sni/docker-compose.yml
@@ -1,8 +1,8 @@
-version: "3"
+version: '3'
 services:
   haproxy:
     image: haproxy
-    ports: ["443:443"]
+    ports: ['443:443']
     volumes:
       - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
       - ./dhparam.pem:/etc/ssl/dhparam.pem:ro
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..b14ba48
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,73 @@
+{
+  "nodes": {
+    "flake-utils": {
+      "inputs": {
+        "systems": "systems"
+      },
+      "locked": {
+        "lastModified": 1710146030,
+        "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
+        "type": "github"
+      },
+      "original": {
+        "id": "flake-utils",
+        "type": "indirect"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1714030708,
+        "narHash": "sha256-JOGPOxa8N6ySzB7SQBsh0OVz+UXZriyahgvfNHMIY0Y=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "b0d52b31f7f4d80f8bf38f0253652125579c35ff",
+        "type": "github"
+      },
+      "original": {
+        "id": "nixpkgs",
+        "type": "indirect"
+      }
+    },
+    "root": {
+      "inputs": {
+        "flake-utils": "flake-utils",
+        "nixpkgs": "nixpkgs",
+        "systems": "systems_2"
+      }
+    },
+    "systems": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nix-systems",
+        "repo": "default",
+        "type": "github"
+      }
+    },
+    "systems_2": {
+      "locked": {
+        "lastModified": 1681028828,
+        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+        "owner": "nix-systems",
+        "repo": "default",
+        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+        "type": "github"
+      },
+      "original": {
+        "id": "systems",
+        "type": "indirect"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..f981ed1
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,26 @@
+{
+  outputs = {
+    self,
+    nixpkgs,
+    flake-utils,
+    systems,
+  }:
+    flake-utils.lib.eachSystem (import systems)
+    (system: let
+      pkgs = import nixpkgs {
+        inherit system;
+      };
+    in {
+      devShells.default = pkgs.mkShell {
+        buildInputs = with pkgs; [
+          gcc
+          go
+          gofumpt
+          gopls
+          gotools
+          go-tools
+          sqlite-interactive
+        ];
+      };
+    });
+}
diff --git a/html/templates/error.html b/html/templates/error.html
index 6094a26..05a5d46 100644
--- a/html/templates/error.html
+++ b/html/templates/error.html
@@ -1,18 +1,12 @@
-<!DOCTYPE html>
+<!doctype html>
 <html class="codeberg-design">
   <head>
     <meta charset="utf-8" />
     <meta name="viewport" content="width=device-width" />
     <title>{{.StatusText}}</title>
 
-    <link
-      rel="stylesheet"
-      href="https://design.codeberg.org/design-kit/codeberg.css"
-    />
-    <link
-      rel="stylesheet"
-      href="https://fonts.codeberg.org/dist/inter/Inter%20Web/inter.css"
-    />
+    <link rel="stylesheet" href="https://design.codeberg.org/design-kit/codeberg.css" />
+    <link rel="stylesheet" href="https://fonts.codeberg.org/dist/inter/Inter%20Web/inter.css" />
 
     <style>
       body {
@@ -34,12 +28,7 @@
     </style>
   </head>
   <body>
-    <svg
-      xmlns="http://www.w3.org/2000/svg"
-      height="10em"
-      viewBox="0 0 24 24"
-      fill="var(--blue-color)"
-    >
+    <svg xmlns="http://www.w3.org/2000/svg" height="10em" viewBox="0 0 24 24" fill="var(--blue-color)">
       <path
         d="M 9 2 C 5.1458514 2 2 5.1458514 2 9 C 2 12.854149 5.1458514 16 9 16 C 10.747998 16 12.345009 15.348024 13.574219 14.28125 L 14 14.707031 L 14 16 L 19.585938 21.585938 C 20.137937 22.137937 21.033938 22.137938 21.585938 21.585938 C 22.137938 21.033938 22.137938 20.137938 21.585938 19.585938 L 16 14 L 14.707031 14 L 14.28125 13.574219 C 15.348024 12.345009 16 10.747998 16 9 C 16 5.1458514 12.854149 2 9 2 z M 9 4 C 11.773268 4 14 6.2267316 14 9 C 14 11.773268 11.773268 14 9 14 C 6.2267316 14 4 11.773268 4 9 C 4 6.2267316 6.2267316 4 9 4 z"
       />
@@ -50,18 +39,13 @@
       <p><b>"{{.Message}}"</b></p>
       <p>
         We hope this isn't a problem on our end ;) - Make sure to check the
-        <a
-          href="https://docs.codeberg.org/codeberg-pages/troubleshooting/"
-          target="_blank"
+        <a href="https://docs.codeberg.org/codeberg-pages/troubleshooting/" target="_blank"
           >troubleshooting section in the Docs</a
         >!
       </p>
     </h5>
     <small class="text-muted">
-      <img
-        src="https://design.codeberg.org/logo-kit/icon.svg"
-        class="align-top"
-      />
+      <img src="https://design.codeberg.org/logo-kit/icon.svg" class="align-top" />
       Static pages made easy -
       <a href="https://codeberg.page">Codeberg Pages</a>
     </small>